Files
homefeed/docker-compose.yml
Salastil c851f11cea Persist the admin API key across restarts instead of regenerating it
The key used to be a fresh randomBytes() on every process start, by
design — but that means every restart invalidates whatever's saved in
a password manager or bookmarked admin session. Now it's generated
once and written to backend/.env (ADMIN_API_KEY=...) on first launch,
then read back from there on every later launch. An explicit
ADMIN_API_KEY env var still wins outright, for Docker deployments that
have no writable .env inside the container — wired an optional
pass-through into both compose files for that case, since without it
they'd keep regenerating on every recreate regardless of this change.

Updated the handful of comments (server.ts, widgets/install.ts,
index.ts's startup banner, .env.example) that documented the old
every-restart-regenerates behavior as a load-bearing security property.
2026-08-14 19:17:28 -04:00

84 lines
3.6 KiB
YAML

services:
# Ingestion, clustering, synthesis, and the public/admin API. SQLite DB, downloaded
# media, and uploaded widgets all persist in the backend-data volume.
backend:
# Also the name `docker compose build`/`push`/`pull` use — Gitea's built-in
# container registry lives on the same host as the git server. Override
# REGISTRY/IMAGE_TAG in .env for a different registry or tag (e.g. a commit
# SHA in CI) without editing this file.
image: ${REGISTRY:-git.salastil.com/salastil}/homefeed-backend:${IMAGE_TAG:-latest}
build:
context: ./backend
environment:
PORT: "4000"
# Must exactly match ORIGIN below — see backend/README.md / root README.md
# "Deploying behind a reverse proxy" for why a mismatch breaks every request
# with a CORS error.
FRONTEND_ORIGIN: ${PUBLIC_ORIGIN:-http://localhost:3000}
DB_PATH: /app/data/homefeed.db
MEDIA_DIR: /app/data/media
WIDGETS_INSTALLED_DIR: /app/data/widgets-installed
NODE_ENV: production
PRIVATE_ACCESS_PASSWORD: ${PRIVATE_ACCESS_PASSWORD:-}
# Optional — the backend persists a generated key to backend/.env on bare-metal
# deploys, but a container has no writable .env of its own (CWD is /app, not the
# backend-data volume), so it'd otherwise regenerate on every recreate. Set this
# once (any random string) to pin a stable key instead.
ADMIN_API_KEY: ${ADMIN_API_KEY:-}
volumes:
- backend-data:/app/data
ports:
# Both containers' ports are published here, not on frontend — see the
# network_mode note on the frontend service below.
- "${BACKEND_PORT:-4000}:4000"
- "${FRONTEND_PORT:-3000}:3000"
extra_hosts:
# Lets the admin point the AI Service host (Connections tab, or PATCH
# /api/admin/settings) at Ollama running on the host machine itself, e.g.
# http://host.docker.internal:11434 — "localhost" from inside this
# container would otherwise mean the container, not the host. Resolves out
# of the box on Docker Desktop already; this makes it work on Linux too.
- "host.docker.internal:host-gateway"
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://localhost:4000/api/categories').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
# The SvelteKit site (adapter-node). Shares the backend container's network
# namespace (network_mode: service:backend) rather than getting its own — so
# "http://localhost:4000" resolves to the backend both from the visitor's browser
# (via the published port above) AND from this container's own Node process when
# it makes server-rendered (SSR) calls to the backend on a page's first load.
# Without this, no single VITE_BACKEND_URL value works for both. This also means
# frontend's own port must be published on backend's ports list above, and
# frontend can't declare its own ports/network settings here.
frontend:
image: ${REGISTRY:-git.salastil.com/salastil}/homefeed-frontend:${IMAGE_TAG:-latest}
build:
context: ./frontend
args:
VITE_BACKEND_URL: ${VITE_BACKEND_URL:-http://localhost:4000}
network_mode: "service:backend"
environment:
PORT: "3000"
HOST: "0.0.0.0"
# Must exactly match FRONTEND_ORIGIN above.
ORIGIN: ${PUBLIC_ORIGIN:-http://localhost:3000}
ADMIN_PANEL_ENABLED: ${ADMIN_PANEL_ENABLED:-false}
depends_on:
backend:
condition: service_healthy
restart: unless-stopped
volumes:
backend-data: