IKHSAN
ENID Baca dalam Bahasa Indonesia

homeio

Public

Forked from doctor-io/homeio

Branch
main
Latest commit
958d74616 October 2026

docker-entrypoint.sh

Open raw
#!/bin/sh
set -e

APP_USER=homeio
DOCKER_SOCKET="${DOCKER_SOCKET_PATH:-/var/run/docker.sock}"

# The app talks to the Docker socket to list containers and to run
# `docker compose` for every install. The socket is owned by a group whose id
# differs per host (docker on Debian, root under Docker Desktop), so the id is
# read at start-up rather than baked into the image. Without this the app runs
# but every Docker call fails with EACCES, and it fails quietly.
if [ -S "$DOCKER_SOCKET" ] && [ "$(id -u)" = "0" ]; then
  SOCKET_GID="$(stat -c '%g' "$DOCKER_SOCKET")"

  if [ "$SOCKET_GID" = "0" ]; then
    # Docker Desktop exposes it as root:root; only the root group can reach it.
    addgroup "$APP_USER" root 2>/dev/null || true
  else
    EXISTING_GROUP="$(getent group "$SOCKET_GID" | cut -d: -f1)"
    if [ -z "$EXISTING_GROUP" ]; then
      EXISTING_GROUP=dockerhost
      addgroup -g "$SOCKET_GID" "$EXISTING_GROUP" 2>/dev/null || true
    fi
    addgroup "$APP_USER" "$EXISTING_GROUP" 2>/dev/null || true
  fi

  echo "Docker socket group: ${SOCKET_GID}"
elif [ ! -S "$DOCKER_SOCKET" ]; then
  echo "WARNING: ${DOCKER_SOCKET} is not mounted — app management will not work."
fi

# The compose file ships a placeholder secret, and production refuses it — so
# `git clone && docker compose up -d`, the quickstart in the README, crash-looped
# on the instrumentation hook with a 500 on /api/health. The Linux installer has
# always generated one; do the same here, and persist it so sessions survive a
# restart. Without a writable state directory the secret is per-boot, which
# signs everyone out on restart but still beats refusing to start.
SECRET_STATE_DIR="${HOMEIO_STATE_DIR:-/stacks}"
SECRET_FILE="${SECRET_STATE_DIR}/.session-secret"
PLACEHOLDER_SECRET="change-me-to-a-random-32-char-secret"

if [ -z "${AUTH_SESSION_SECRET:-}" ] || [ "${AUTH_SESSION_SECRET}" = "${PLACEHOLDER_SECRET}" ]; then
  if [ -s "$SECRET_FILE" ]; then
    AUTH_SESSION_SECRET="$(cat "$SECRET_FILE")"
  else
    AUTH_SESSION_SECRET="$(od -An -tx1 -N32 /dev/urandom | tr -d ' \n')"
    if mkdir -p "$SECRET_STATE_DIR" 2>/dev/null &&
       printf '%s' "$AUTH_SESSION_SECRET" > "$SECRET_FILE" 2>/dev/null; then
      chmod 600 "$SECRET_FILE" 2>/dev/null || true
      [ "$(id -u)" = "0" ] && chown "$APP_USER" "$SECRET_FILE" 2>/dev/null
      echo "Generated a session secret and stored it at ${SECRET_FILE}."
    else
      echo "WARNING: could not persist a session secret to ${SECRET_FILE};" \
           "a new one is generated on every start, so sessions will not survive a restart."
    fi
  fi
fi
export AUTH_SESSION_SECRET

# Default V8 heap cap is generous for amd64 (1.5 GB) but exceeds the
# total RAM of a Pi 3 (1 GB). Apply a Pi-friendly default unless the
# operator overrides via NODE_OPTIONS.
if [ -z "${NODE_OPTIONS:-}" ]; then
  NODE_OPTIONS="--max-old-space-size=768"
fi
export NODE_OPTIONS

if [ -f "dist-server/server.js" ]; then
  SERVER_ENTRY=dist-server/server.js
else
  SERVER_ENTRY=server.js
fi

echo "Running database migrations..."

if [ "$(id -u)" = "0" ]; then
  su-exec "$APP_USER" node_modules/.bin/drizzle-kit push --config drizzle.config.ts
  exec su-exec "$APP_USER" node "$SERVER_ENTRY"
fi

node_modules/.bin/drizzle-kit push --config drizzle.config.ts
exec node "$SERVER_ENTRY"
bytes
3,307
lines
83
Type
sh
Latest commit
16 October 2026