diff --git a/deploy/.env.example b/deploy/.env.example index e85ddcb..fc2b906 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -1,20 +1,15 @@ -# MealQuest production environment. Copy to deploy/.env (never committed) -# and replace the placeholders. The service fails fast: a missing or invalid -# required variable stops it at boot with the variable named. +# MealQuest production environment: the template for deploy/.env (never +# committed). The service fails fast: a missing or invalid required variable +# stops it at boot with the variable named. Creating deploy/.env: CTXD +# project mealquest, RUNBOOKS.MD "Stack and first deploy" > "3. Environment +# file". # # Secrets are never set here. They live in deploy/secrets/ (never committed), -# one value per file, owned by the container user and readable only by it: -# -# mkdir -p deploy/secrets && chmod 700 deploy/secrets -# head -c 32 /dev/urandom | base64 > deploy/secrets/secret_key -# printf '%s\n' '' > deploy/secrets/oidc_client_secret -# printf '%s\n' '' > deploy/secrets/google_client_secret -# sudo chown 10001:10001 deploy/secrets/* && sudo chmod 600 deploy/secrets/* -# -# SearXNG's secret key is the exception: compose reads it itself, so it -# stays owned by you (mode 0600). Create it after the chown above: -# -# (umask 077; printf 'SEARXNG_SECRET=%s\n' "$(openssl rand -hex 32)" > deploy/secrets/searxng.env) +# one value per file, owned by the container user 10001:10001 with mode +# 0600: secret_key, oidc_client_secret and google_client_secret. SearXNG's +# searxng.env is the exception: compose reads it itself, so it is owned by +# the host account that runs compose, mode 0600. Creating them: CTXD project +# mealquest, RUNBOOKS.MD "Stack and first deploy" > "4. Secrets". # # deploy/compose.yaml mounts them at /run/secrets/ and points the # *_FILE variables at them. @@ -42,8 +37,7 @@ MEALQUEST_TRUSTED_PROXY=10.60.1.33 # IANA zone the household settings are created with on first boot. Required # until those settings exist; ignored afterwards (change the zone in -# household settings instead). Confirm the household's zone before the first -# deploy. +# household settings instead). MEALQUEST_DEFAULT_TZ=America/New_York # --- Authentik OIDC ---------------------------------------------------------- @@ -88,7 +82,8 @@ MEALQUEST_SESSION_MAX=4320h # # Added to deploy/compose.yaml and this file by the features that use them. # -# Web Push (Feature 19). Generate the key pair with cmd/vapidgen. +# Web Push (Feature 19). Key pair: CTXD project mealquest, RUNBOOKS.MD +# "VAPID keys (Web Push)". # MEALQUEST_VAPID_PUBLIC_KEY= # MEALQUEST_VAPID_PRIVATE_KEY_FILE=/run/secrets/vapid_private_key # MEALQUEST_VAPID_SUBJECT=mailto: diff --git a/deploy/compose.dev.yaml b/deploy/compose.dev.yaml index ee071ba..3515700 100644 --- a/deploy/compose.dev.yaml +++ b/deploy/compose.dev.yaml @@ -1,8 +1,7 @@ -# Development override: publishes SearXNG on the dev machine's loopback so -# it can be queried directly, e.g. -# -# docker compose -f deploy/compose.yaml -f deploy/compose.dev.yaml up -d searxng -# curl 'http://127.0.0.1:8888/search?q=chicken+tikka+masala+recipe&format=json' +# Development override layered on deploy/compose.yaml: publishes SearXNG +# on the dev machine's loopback (127.0.0.1:8888) so it can be queried +# directly. Using it: CTXD project mealquest, RUNBOOKS.MD "Local +# development". # # Loopback only, never a LAN address. Never used in production: the # systemd unit starts deploy/compose.yaml alone. diff --git a/deploy/compose.yaml b/deploy/compose.yaml index ca353ad..a85f106 100644 --- a/deploy/compose.yaml +++ b/deploy/compose.yaml @@ -1,18 +1,16 @@ -# Production stack for MealQuest on 10.60.1.14 (D-018). Build on the host, -# then start through systemd: -# -# docker compose -f deploy/compose.yaml build -# sudo systemctl restart mealquest +# Production stack for MealQuest on 10.60.1.14 (D-018). Building, deploying +# and operating it: CTXD project mealquest, RUNBOOKS.MD "Stack unit" and +# "Routine deploy and rollback". # # The services use restart: "no" on purpose: Docker must not start them # before the DOCKER-USER firewall is applied. deploy/systemd/mealquest.service # owns start and stop and requires mealquest-firewall.service first. # -# Configuration comes from deploy/.env (copy deploy/.env.example) and secrets -# from deploy/secrets/; neither is committed. Compose bind-mounts file secrets -# with their host owner and mode, so each file in deploy/secrets/ must be -# owned by the container user 10001:10001 with mode 0600 (except -# searxng.env, read by compose itself; see the searxng service). +# Configuration comes from deploy/.env (template: deploy/.env.example) and +# secrets from deploy/secrets/; neither is committed. Compose bind-mounts +# file secrets with their host owner and mode, so each file in +# deploy/secrets/ must be owned by the container user 10001:10001 with mode +# 0600 (except searxng.env, read by compose itself; see the searxng service). name: mealquest services: @@ -65,9 +63,8 @@ services: # the app's own user (owner of the volume) with a read-only root # filesystem, no privilege escalation and no Linux capabilities. # - # Restore: follow "Restore the database" in the CTXD project mealquest, - # RUNBOOKS.MD (stop the stack, move the database with its -wal and -shm - # aside, restore, check). + # Restoring the database: CTXD project mealquest, RUNBOOKS.MD "Restore + # the database". litestream: image: litestream/litestream:0.5.17 user: "10001:10001" diff --git a/deploy/systemd/mealquest-firewall.service b/deploy/systemd/mealquest-firewall.service index c7c5f8e..90d2fdc 100644 --- a/deploy/systemd/mealquest-firewall.service +++ b/deploy/systemd/mealquest-firewall.service @@ -5,8 +5,9 @@ # are one-way: a failure here fails this unit visibly and never fails # docker.service. # -# Start MealQuest only after this unit has succeeded: -# systemctl start mealquest-firewall && docker compose up -d +# mealquest.service requires this unit and starts the stack only after it +# has succeeded. Installing and operating both: CTXD project mealquest, +# RUNBOOKS.MD "Host firewall" and "Stack unit". [Unit] Description=Restrict MealQuest port 8090 to Traefik (DOCKER-USER) After=docker.service diff --git a/deploy/systemd/mealquest-media-backup.service b/deploy/systemd/mealquest-media-backup.service index 9f5a61c..9222684 100644 --- a/deploy/systemd/mealquest-media-backup.service +++ b/deploy/systemd/mealquest-media-backup.service @@ -1,8 +1,7 @@ # Runs the nightly MealQuest media backup (/usr/local/sbin/mealquest-media-backup). -# Triggered by mealquest-media-backup.timer; run by hand with -# systemctl start mealquest-media-backup -# and read the result with -# journalctl -u mealquest-media-backup +# Triggered by mealquest-media-backup.timer; output goes to the journal. +# Installing it and running it by hand: CTXD project mealquest, +# RUNBOOKS.MD "Media — nightly rsync". # # The script runs as the app's user (10001, host user "mealquest") with no # capabilities. systemd, as root, only bind-mounts the data volume's root diff --git a/deploy/systemd/mealquest-media-backup.sh b/deploy/systemd/mealquest-media-backup.sh index 94169f1..fd99363 100755 --- a/deploy/systemd/mealquest-media-backup.sh +++ b/deploy/systemd/mealquest-media-backup.sh @@ -16,8 +16,8 @@ # directory are copied as links (-a), never followed. # # The media directory is created at deploy time (CTXD project mealquest, -# RUNBOOKS.MD). If it is missing the job fails; it is never treated as -# "nothing to back up". +# RUNBOOKS.MD "Media — nightly rsync"). If it is missing the job fails; it +# is never treated as "nothing to back up". # # rsync mirrors into .../mealquest-backups/media/ only: the target ends in # a slash, so --delete removes files from that directory and nothing