valknar 2e31c1dcc9 fix(passbolt): persist GPG keyring as volume to survive restarts
Passbolt's entrypoint creates /var/lib/passbolt/.gnupg/pubring.kbx
as root while PHP-FPM runs as www-data. Without a volume this file
is recreated with wrong ownership on every container recreate, breaking
all GPG operations. Mounting the dir as a volume keeps the chown
33:33 fix permanent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 21:44:20 +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
affine Collaborative workspace & notes affine, redis, db
n8n Workflow automation & notification relay n8n, db
gitea Git hosting + CI runner gitea, runner, db
coolify Deployment platform coolify, realtime, redis, db
vaultwarden Password manager (legacy) vaultwarden
passbolt Password manager (GPG-encrypted, team sharing) passbolt, db

Tools

Directory Description
_backup Daily restic backups to HiDrive (host script + systemd timer)
_update Nightly image update check + prune (host script + systemd timer)

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
ssh vps 'ln -sf ~/stacks/_backup/stacks-backup.service /etc/systemd/system/ && \
         ln -sf ~/stacks/_backup/stacks-backup.timer /etc/systemd/system/ && \
         systemctl daemon-reload && systemctl enable --now stacks-backup.timer'

# Manual test run
ssh vps '~/stacks/_backup/backup.sh'

# Check timer status
ssh vps 'systemctl status stacks-backup.timer'

# View snapshots
ssh vps 'source ~/stacks/_backup/.env && restic -r /mnt/hidrive/users/valknar/Backup/stacks 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/
ssh vps 'chmod +x ~/stacks/_update/update.sh'

# Install systemd units
ssh vps 'ln -sf ~/stacks/_update/stacks-update.service /etc/systemd/system/ && \
         ln -sf ~/stacks/_update/stacks-update.timer /etc/systemd/system/ && \
         systemctl daemon-reload && systemctl enable --now stacks-update.timer'

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

# Check timer status
ssh vps 'systemctl status stacks-update.timer'

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 299 KiB
Languages
Shell 95.9%
Go Template 4.1%