2026-02-15 22:41:50 +01:00
# 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/<stack>/` .
## 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 |
2026-06-08 20:36:58 +02:00
| `n8n` | Workflow automation & notification relay | n8n, db |
2026-02-15 22:41:50 +01:00
| `gitea` | Git hosting + CI runner | gitea, runner, db |
| `coolify` | Deployment platform | coolify, realtime, redis, db |
2026-06-09 20:00:05 +02:00
| `passbolt` | Password manager (GPG-encrypted, team sharing) | passbolt, db |
2026-06-12 18:18:17 +02:00
| `code` | Browser-based VS Code IDE with Anthropic API access | code |
2026-02-15 22:41:50 +01:00
2026-02-16 07:14:32 +01:00
## Tools
2026-06-16 20:56:04 +02:00
| File | Description |
2026-02-16 07:14:32 +01:00
|---|---|
2026-06-16 20:56:04 +02:00
| `stacks.sh` | CLI to manage stacks, services, scaffolding, updates, and backups |
| `.env` | Root config: `WEBHOOK_URL` , `RESTIC_REPOSITORY` , `RESTIC_PASSWORD` (gitignored) |
| `.env.example` | Template for the root `.env` |
2026-02-16 07:14:32 +01:00
2026-06-16 19:55:05 +02:00
## stacks.sh
`stacks.sh` is the primary management CLI. It wraps `docker compose` with glob-based multi-stack targeting, manages the update and backup systemd services, scaffolds new stacks, and generates shell completions.
``` bash
./stacks.sh help
```
**Stack commands ** — all accept one or more stack names or glob patterns (omit for all stacks):
``` bash
./stacks.sh ls # list all stacks with live container status
./stacks.sh ps gitea # container status table
./stacks.sh up # start all stacks
./stacks.sh up gitea traefik # start specific stacks
./stacks.sh down 'g*' # stop stacks matching glob
./stacks.sh restart 'g*,traefik' # glob + exact name, comma-separated
./stacks.sh pull --parallel # pull all images in parallel
./stacks.sh logs -f gitea # follow logs
./stacks.sh logs -n 100 gitea n8n # tail multiple stacks
./stacks.sh exec gitea gitea gitea admin user list # exec in container
./stacks.sh run passbolt passbolt bin/cake passbolt healthcheck
```
2026-06-16 20:56:04 +02:00
**Service management ** (reads `WEBHOOK_URL` , `RESTIC_REPOSITORY` , `RESTIC_PASSWORD` from root `.env` ):
2026-06-16 19:55:05 +02:00
``` bash
2026-06-16 20:56:04 +02:00
./stacks.sh update install # write & enable systemd update timer
2026-06-16 19:55:05 +02:00
./stacks.sh update run # run update now
./stacks.sh update status # show timer/service status
./stacks.sh update logs # show journal logs
2026-06-16 20:56:04 +02:00
./stacks.sh backup install # write & enable systemd backup timer
./stacks.sh backup run # run backup now (auto-detects <stack>_db containers)
2026-06-16 19:55:05 +02:00
./stacks.sh backup snapshots # list restic snapshots
```
**Scaffold a new stack: **
``` bash
./stacks.sh new myapp # basic stack with Traefik labels
./stacks.sh new myapp --db postgres # with Postgres service
./stacks.sh new myapp --db postgres --redis # with Postgres + Redis
./stacks.sh new myapp --no-traefik # expose port instead of Traefik
```
Generates `compose.yml` (with healthchecks, `../.data/` volumes, Traefik labels) and `.env.example` .
**Shell completion: **
``` bash
# Dynamic — discovers stacks at tab-complete time (default)
./stacks.sh completion zsh --install
# Static — bakes current stack list in; useful on the VPS
./stacks.sh completion zsh --static --install
```
**Global flags: ** `--dry-run` , `--parallel` , `--verbose` , `--quiet`
2026-02-15 22:41:50 +01:00
## Deployment
``` bash
2026-06-08 18:56:55 +02:00
# Copy example env and fill in secrets
cp <stack>/.env.example <stack>/.env
2026-02-15 22:41:50 +01:00
# Sync a stack to VPS
rsync -avz <stack>/ vps:~/stacks/<stack>/
# Start a stack
ssh vps 'cd ~/stacks/<stack> && docker compose up -d'
```
## Network
All stacks share the external `falcon_network` Docker network for inter-service communication (e.g. traefik routing, mailpit SMTP).
2026-02-16 07:14:32 +01:00
## Backup
2026-06-16 20:56:04 +02:00
Runs daily at 3:00 AM via a systemd timer. Detects Postgres databases automatically by convention (`<stack>_db` container, user `<stack>` , database `<stack>` ), dumps each one, then runs a full restic backup of `.data/` . Retention: 7 daily, 4 weekly, 6 monthly. Notifications go to Telegram via n8n.
2026-02-16 07:14:32 +01:00
``` bash
2026-06-16 20:56:04 +02:00
# First-time setup on VPS
2026-06-16 21:13:00 +02:00
cp .env.example .env && $EDITOR .env # set RESTIC_REPOSITORY, RESTIC_PASSWORD, WEBHOOK_URL
source .env && restic init # initialise restic repo
2026-06-16 20:56:04 +02:00
./stacks.sh backup install # write & enable systemd unit + timer
./stacks.sh backup run # test run
./stacks.sh backup snapshots # list snapshots
./stacks.sh backup status # timer/service status
./stacks.sh backup logs # journald logs
2026-02-16 07:14:32 +01:00
```
2026-06-09 19:42:25 +02:00
## Updates
2026-06-16 20:56:04 +02:00
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.
2026-06-09 19:42:25 +02:00
``` bash
2026-06-16 20:56:04 +02:00
./stacks.sh update install # write & enable systemd unit + timer
./stacks.sh update run # test run
./stacks.sh update status # timer/service status
./stacks.sh update logs # journald logs
2026-06-09 19:42:25 +02:00
```
2026-06-08 22:30:40 +02:00
## Notifications
2026-06-16 21:13:00 +02:00
Both update and backup POST to an n8n webhook on completion, which forwards the message to Telegram.
2026-06-08 22:30:40 +02:00
2026-06-16 21:13:00 +02:00
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.
2026-06-08 22:30:40 +02:00
2026-02-15 22:41:50 +01:00
## Data
Persistent data is stored in `~/stacks/.data/<stack>/` on the VPS using bind mounts. Database stacks use dedicated Postgres instances with simple credentials.