The patched PublicKeyValidationService.php and its volume mount are no longer needed now that the metadata key exists in the database. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 |
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/.env→WEBHOOK_URL_update/.env→WEBHOOK_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.