# Stacks Self-contained Docker Compose stacks for `pivoine.art` infrastructure. Each stack is independently deployable with its own `compose.yml` and `.env`. All persistent data lives in `../.data//`. ## Stacks | Stack | Description | Services | |---|---|---| | `traefik` | Reverse proxy, TLS termination | traefik | | `mailpit` | SMTP relay (no web UI) | mailpit | | `umami` | Web analytics | umami, db | | `immich` | Photo & video management | immich, ml, redis, db | | `n8n` | Workflow automation & notification relay | n8n, db | | `gitea` | Git hosting + CI runner | gitea, runner, db | | `coolify` | Deployment platform | coolify, realtime, redis, db | | `passbolt` | Password manager (GPG-encrypted, team sharing) | passbolt, db | | `code` | Browser-based VS Code IDE with Anthropic API access | code | | `headscale` | Self-hosted Tailscale control server (WireGuard mesh VPN) | headscale | | `pulsenode` | Infrastructure dashboard - monitors all the stacks above | pulsenode | ## Config files | File | Description | |---|---| | `.env` | Root config: `WEBHOOK_URL`, `RESTIC_REPOSITORY`, `RESTIC_PASSWORD` (gitignored) | | `.env.example` | Template for the root `.env` | ## Management Stacks are managed via `stacks.sh` from [git@dev.pivoine.art:valknar/scripts.git](ssh://git@dev.pivoine.art/valknar/scripts.git), available as `stacks` in PATH. **Stack commands** — all accept one or more stack names or glob patterns (omit for all stacks): ```bash stacks ls # list all stacks with live container status stacks ps gitea # container status table stacks up # start all stacks stacks up gitea traefik # start specific stacks stacks down 'g*' # stop stacks matching glob stacks restart 'g*,traefik' # glob + exact name, comma-separated stacks pull --parallel # pull all images in parallel stacks logs -f gitea # follow logs stacks logs -n 100 gitea n8n # tail multiple stacks stacks exec gitea gitea gitea admin user list # exec in container stacks run passbolt passbolt bin/cake passbolt healthcheck ``` **Service management** (reads `WEBHOOK_URL`, `RESTIC_REPOSITORY`, `RESTIC_PASSWORD` from root `.env`): ```bash stacks update install # write & enable systemd update timer stacks update run # run update now stacks update status # show timer/service status stacks update logs # show journal logs stacks backup install # write & enable systemd backup timer stacks backup run # run backup now (auto-detects _db containers) stacks backup snapshots # list restic snapshots ``` **Scaffold a new stack:** ```bash stacks new myapp # basic stack with Traefik labels stacks new myapp --db postgres # with Postgres service stacks new myapp --db postgres --redis # with Postgres + Redis stacks new myapp --no-traefik # expose port instead of Traefik ``` Generates `compose.yml` (with healthchecks, `../.data/` volumes, Traefik labels) and `.env.example`. **Global flags:** `--dry-run`, `--parallel`, `--verbose`, `--quiet` ## Deployment ```bash # Copy example env and fill in secrets cp /.env.example /.env # Sync a stack to VPS rsync -avz / vps:~/stacks// # Start a stack ssh vps 'stacks up ' ``` ## Network All stacks share the external `falcon_network` Docker network for inter-service communication (e.g. traefik routing, mailpit SMTP). ## Backup Runs daily at 3:00 AM via a systemd timer. Detects Postgres databases automatically by convention (`_db` container, user ``, database ``), dumps each one, then runs a full restic backup of `.data/`. Retention: 7 daily, 4 weekly, 6 monthly. Notifications go to Telegram via n8n. ```bash # First-time setup on VPS cp .env.example .env && $EDITOR .env # set RESTIC_REPOSITORY, RESTIC_PASSWORD, WEBHOOK_URL source .env && restic init # initialise restic repo stacks backup install # write & enable systemd unit + timer stacks backup run # test run stacks backup snapshots # list snapshots stacks backup status # timer/service status stacks backup logs # journald logs ``` ## Updates Runs nightly at 2:00 AM via a systemd timer. Pulls the latest image for every stack, recreates any container whose image changed, prunes dangling images, and sends a Telegram notification via n8n. ```bash stacks update install # write & enable systemd unit + timer stacks update run # test run stacks update status # timer/service status stacks update logs # journald logs ``` ## Notifications Both update and backup POST to an n8n webhook on completion, which forwards the message to Telegram. The webhook URL is set via `WEBHOOK_URL` in the root `.env`. Both services point to the same n8n workflow at `https://n8n.pivoine.art`, which accepts `{ "message": "...", "color": "..." }` and forwards it to Telegram. ## Data Persistent data is stored in `~/stacks/.data//` on the VPS using bind mounts. Database stacks use dedicated Postgres instances with simple credentials.