From e70bf748470c416145d1bddf1c617c1421f787cd Mon Sep 17 00:00:00 2001 From: DenDanskeMine Date: Sun, 26 Jul 2026 15:21:26 +0000 Subject: [PATCH] Stop installing the dev docker-compose unit on production hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14. A production install provisions PostgreSQL and Redis natively — install.sh enables the distro services and creates the role and database with psql. But it also ran `make install-services`, which links the DEV unit set, and that includes `danbyte-infra`: `docker compose up -d postgres redis`. Then danbyte-web and danbyte-workers both declared `Wants=danbyte-infra.service`, so enabling them started the container stack. The result on a host that already ran PostgreSQL: an idle container with an empty database, publishing 5432 against the server Danbyte is actually using. Harmless as observed, but the two compete for the port and whichever starts first wins it — so a reboot could have pointed Danbyte at the empty container, migrated into it, and looked exactly like total data loss. - Makefile: split the unit list into DEV_SERVICES (mockups, infra, backend) and SHARED_SERVICES (workers, docs). PROD_SERVICES is now the full production set, and install-prod-services links and enables the timers too, since install.sh no longer calls install-services for them. - install.sh: link PROD_SERVICES only. - danbyte-web, danbyte-workers: drop `Wants=danbyte-infra.service`. It is a dev convenience and has no business in a unit shipped to production; danbyte-backend (dev-only) keeps it, and `make up` starts infra explicitly. - Docs: state that production uses native PostgreSQL/Redis and that compose is development only, how to point an install at an existing server, and how to remove the stray unit and container from an install made before this fix. Existing installs are unaffected until re-run; the docs note covers cleanup. --- Makefile | 20 ++++++++++++++++++-- docs/getting-started/installation.md | 24 +++++++++++++++++++++++- scripts/install.sh | 6 +++++- services/danbyte-web.service | 1 - services/danbyte-workers.service | 1 - 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index f1f1c727..3114b9e6 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,14 @@ SERVICE_HOME ?= /opt/danbyte # Where the app writes danbyte.log + gunicorn logs (settings.LOGGING reads # DANBYTE_LOG_DIR from .env; systemd still mirrors process output to journald). LOG_DIR ?= /var/log/danbyte -SERVICES := danbyte-mockups danbyte-infra danbyte-backend danbyte-workers danbyte-docs +# Dev-only units. `danbyte-infra` runs Postgres + Redis in docker compose for a +# workstation; a production install provisions them natively (scripts/install.sh +# creates the role/database with psql), so these must NEVER be linked there — +# doing so left an idle, empty Postgres container on hosts that already had one. +DEV_SERVICES := danbyte-mockups danbyte-infra danbyte-backend +# Units both dev and production run. +SHARED_SERVICES := danbyte-workers danbyte-docs +SERVICES := $(DEV_SERVICES) $(SHARED_SERVICES) # Timer-driven oneshots (monitoring beat). Each has a .service + a .timer; the # timer is what gets enabled. Not part of `up`/`down` (they're not long-running). TIMERS := danbyte-dispatch danbyte-materialise danbyte-prune danbyte-utilization danbyte-alert-maintenance danbyte-discover danbyte-cleanup danbyte-drift-dispatch danbyte-auto-upgrade danbyte-drive-outposts danbyte-digest @@ -298,7 +305,9 @@ collectstatic: # make install-prod-services # systemctl --user stop danbyte-backend danbyte-frontend # the dev units # systemctl --user enable --now danbyte-web danbyte-ws danbyte-frontend-prod -PROD_SERVICES := danbyte-web danbyte-ws danbyte-frontend-prod +# Everything a production host runs — web/ws/frontend plus the shared units. +# scripts/install.sh links ONLY these; it must not pull in DEV_SERVICES. +PROD_SERVICES := danbyte-web danbyte-ws danbyte-frontend-prod $(SHARED_SERVICES) install-prod-services: @mkdir -p $(SYSTEMD_DIR) @@ -306,7 +315,14 @@ install-prod-services: ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \ echo " linked $$s.service" ; \ done + @for s in $(TIMERS); do \ + ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \ + ln -sfn $(PROJECT_DIR)/services/$$s.timer $(SYSTEMD_DIR)/$$s.timer ; \ + done @systemctl --user daemon-reload + @for s in $(TIMERS); do \ + systemctl --user enable --now $$s.timer >/dev/null 2>&1 || true ; \ + done @echo "Linked. Build the frontend + collect static, then enable:" @echo " make frontend-build collectstatic" @echo " systemctl --user enable --now $(PROD_SERVICES)" diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index d9c756c6..6f2ff9eb 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -104,7 +104,29 @@ The other tabs cover building from source and a local dev checkout. 3. Deploys the app to `/danbyte` and builds the venv from the bundle's vendored CPython 3.13 + wheelhouse (no internet needed). 4. **Generates secrets** with Python's CSPRNG and writes a `chmod 600`, service-user-owned `.env` — `DJANGO_SECRET_KEY` + `MONITORING_SECRET_KEY` (~400-bit), a 24-char DB password, and a 20-char admin password. 5. Creates the PostgreSQL role + database, runs migrations, and bootstraps the `admin` superuser. - 6. Installs the systemd units (web, workers, websocket, timers), writes logs to `/var/log/danbyte`, and — unless `--no-nginx` — puts nginx + TLS in front. + 6. Installs the systemd units (web, workers, websocket, docs, timers), writes logs to `/var/log/danbyte`, and — unless `--no-nginx` — puts nginx + TLS in front. + 7. Prints the generated **admin password** at the end. + + !!! note "PostgreSQL and Redis are native, not containers" + + A production install uses the distro's `postgresql` and + `redis-server` and creates the role and database with `psql`. The + `docker-compose.yml` in the repo and the `danbyte-infra` unit that + runs it are **development only** — they are never installed on a + production host. If you already run PostgreSQL, the installer uses + it: point `DB_HOST` / `DB_PORT` / `DB_NAME` / `DB_USER` / + `DB_PASSWORD` in `.env` at your server and re-run + `manage.py migrate`. + + Installs made before this was fixed may have a stray + `danbyte-infra` unit and an unused Postgres container. Remove them + with: + + ```bash + sudo -u danbyte systemctl --user disable --now danbyte-infra + rm -f ~danbyte/.config/systemd/user/danbyte-infra.service + ``` + 7. Prints the generated **admin password** at the end. **Where things land** diff --git a/scripts/install.sh b/scripts/install.sh index 7f7f9102..683311ff 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -254,7 +254,11 @@ as_user bash -lc "cd '$APP' && .venv/bin/python manage.py migrate --noinput \ # ── 9. systemd units ───────────────────────────────────────────────────────── step "Installing + (re)starting services" -as_user bash -lc "cd '$APP' && make install-services install-prod-services >/dev/null" +# ONLY the production unit set. This used to also run `install-services`, which +# links the dev units — including danbyte-infra, the docker-compose Postgres + +# Redis stack. On a host that already ran Postgres that left an idle, empty +# container competing for 5432 (issue #14). +as_user bash -lc "cd '$APP' && make install-prod-services >/dev/null" DANBYTE_UNITS="danbyte-web danbyte-ws danbyte-frontend-prod danbyte-workers danbyte-docs" # enable = start at boot; restart = pick up freshly-deployed code (a plain # `enable --now` is a no-op on already-running units, so a re-install/upgrade diff --git a/services/danbyte-web.service b/services/danbyte-web.service index 8f3fb4f2..2e145328 100644 --- a/services/danbyte-web.service +++ b/services/danbyte-web.service @@ -4,7 +4,6 @@ # WebSockets are served by danbyte-ws (daphne), not here. Description=Danbyte web (gunicorn WSGI, 127.0.0.1:8000) After=network.target -Wants=danbyte-infra.service StartLimitIntervalSec=0 [Service] diff --git a/services/danbyte-workers.service b/services/danbyte-workers.service index 9a1277aa..9100cec8 100644 --- a/services/danbyte-workers.service +++ b/services/danbyte-workers.service @@ -1,7 +1,6 @@ [Unit] Description=Danbyte RQ worker pool (default, high, low queues) After=network.target -Wants=danbyte-infra.service [Service] Type=simple