valknar e3cd2df372 docs: document stacks.sh in README
Replace manual docker compose / systemctl snippets with stacks.sh
equivalents and add a dedicated section covering all commands.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 19:55:05 +02:00
2026-06-10 13:43:21 +02:00
2026-06-16 19:55:05 +02: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
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

Tools

File/Directory Description
stacks.sh CLI to manage stacks, services, and scaffolding
_backup Daily restic backups to HiDrive (host script + systemd timer)
_update Nightly image update check + prune (host script + systemd timer)

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.

./stacks.sh help

Stack commands — all accept one or more stack names or glob patterns (omit for all stacks):

./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

Service management:

./stacks.sh update install     # link & enable systemd update timer
./stacks.sh update run         # run update now
./stacks.sh update status      # show timer/service status
./stacks.sh update logs        # show journal logs

./stacks.sh backup install     # link & enable systemd backup timer
./stacks.sh backup run         # run backup now
./stacks.sh backup snapshots   # list restic snapshots

Scaffold a new stack:

./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:

# 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

Deployment

# Copy example env and fill in secrets
cp <stack>/.env.example <stack>/.env

# 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).

Backup

The _backup stack runs a daily restic backup at 3:00 AM. It dumps all Postgres databases, then backs up the entire .data/ directory to HiDrive. Retention: 7 daily, 4 weekly, 6 monthly snapshots. Notifications go to Telegram via n8n.

# Deploy backup stack
rsync -avz _backup/ vps:~/stacks/_backup/

# Initialize restic repo (first time only)
ssh vps 'source ~/stacks/_backup/.env && restic init -r /mnt/hidrive/users/valknar/Backup/stacks'

# Install systemd units (or use stacks.sh on the VPS)
ssh vps '~/stacks/stacks.sh backup install'

# Manual run / status
ssh vps '~/stacks/stacks.sh backup run'
ssh vps '~/stacks/stacks.sh backup status'
ssh vps '~/stacks/stacks.sh backup snapshots'

Updates

The _update script runs nightly at 2:00 AM. It pulls the latest image for every stack, recreates any containers whose image changed, prunes dangling images, and sends a Telegram notification via n8n.

# Deploy update stack
rsync -avz _update/ vps:~/stacks/_update/

# Install systemd units (or use stacks.sh on the VPS)
ssh vps '~/stacks/stacks.sh update install'

# Manual run / status
ssh vps '~/stacks/stacks.sh update run'
ssh vps '~/stacks/stacks.sh update status'

Notifications

The update script and the backup script both POST to an n8n webhook, which forwards messages to Telegram.

The webhook URL is set in:

  • _backup/.envWEBHOOK_URL
  • _update/.envWEBHOOK_URL

Both point to the same n8n workflow at https://n8n.pivoine.art. The workflow accepts { "message": "..." } and forwards it to Telegram.

Data

Persistent data is stored in ~/stacks/.data/<stack>/ on the VPS using bind mounts. Database stacks use dedicated Postgres instances with simple credentials.

S
Description
Self-contained Docker Compose stacks for pivoine.art infrastructure.
Readme 359 KiB
Languages
Shell 99.6%
Go Template 0.4%