Added comprehensive backup solution to The Falcon infrastructure:
- **Restic Stack** (restic.pivoine.art):
- Backrest web UI for managing restic backups
- Automated scheduled backups with retention policies
- Real-time backup status and monitoring
- Restore capabilities via web interface
- **Backup Configuration**:
- Target: /mnt/hidrive/users/valknar/Backup
- Backs up all critical Docker volumes read-only:
- PostgreSQL, Redis, Directus (uploads/bundle)
- Awesome, Gotify, Scrapy (data/code)
- n8n workflows, Filestash state
- Linkwarden bookmarks/search index
- Let's Encrypt SSL certificates
- **Infrastructure Updates**:
- Added RESTIC_* environment variables to arty.yml
- Updated compose.yaml to include restic stack
- Updated README.md and CLAUDE.md documentation
- Configured Traefik routing with SSL
All volumes mounted read-only to backup container for safety.
Backrest data persisted across: data, config, cache, tmp volumes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
Multi-service Docker Compose stack named "falcon" managing production services on pivoine.art domain. Uses Arty for configuration management with centralized environment variables and custom scripts.
Architecture
Compose Include Pattern
Root compose.yaml uses Docker Compose's include directive to orchestrate multiple service stacks:
- core: Shared PostgreSQL 16 + Redis 7 infrastructure
- proxy: Traefik reverse proxy with Let's Encrypt SSL
- sexy: Directus 11 CMS + SvelteKit frontend
- awsm: Next.js application with SQLite
- track: Umami analytics (PostgreSQL)
- gotify: Push notification server
- scrapy: Scrapyd web scraping cluster (scrapyd, scrapy, scrapyrt)
- n8n: Workflow automation platform (PostgreSQL)
- stash: Filestash web-based file manager
- links: Linkwarden bookmark manager (PostgreSQL + Meilisearch)
- restic: Backrest backup system with restic backend
- sablier: Dynamic scaling plugin for Traefik
- vpn: WireGuard VPN (wg-easy)
All services connect to a single external Docker network (falcon_network by default, defined by $NETWORK_NAME).
Environment Management via Arty
Configuration is centralized in arty.yml:
- envs.default: All environment variables with sensible defaults
- scripts: Common Docker Compose and operational commands
- Variables follow naming pattern:
{SERVICE}_COMPOSE_PROJECT_NAME,{SERVICE}_TRAEFIK_HOST, etc.
Sensitive values (passwords, secrets) live in .env and override arty.yml defaults.
Traefik Routing & Security Architecture
Services expose themselves via Docker labels:
- HTTP → HTTPS redirect on
webentrypoint (port 80) - SSL termination on
web-secureentrypoint (port 443) - Let's Encrypt certificates stored in
proxyvolume - Path-based routing:
/apiroutes to Directus backend, root to frontend - Compression middleware applied via labels
- All routers scoped to
$NETWORK_NAMEnetwork
Security Features:
- TLS Security: Minimum TLS 1.2, strong cipher suites (ECDHE, AES-GCM, ChaCha20), SNI strict mode
- Security Headers: HSTS (1-year), X-Frame-Options, X-XSS-Protection, Content-Type-Options, Referrer-Policy, Permissions-Policy
- Dynamic Configuration: Security settings in
proxy/dynamic/security.yamlwith auto-reload - Rate Limiting: Available middlewares (100 req/s general, 30 req/s API)
- HTTP Basic Auth: Scrapyd protected with username/password authentication
Database Initialization
core/postgres/init/01-init-databases.sh runs on first PostgreSQL startup:
- Creates
directusdatabase for Sexy CMS - Creates
umamidatabase for Track analytics - Creates
n8ndatabase for workflow automation - Creates
linkwardendatabase for Links bookmark manager - Grants privileges to
$DB_USER
Common Commands
All commands use pnpm arty (leveraging scripts in arty.yml):
Stack Management
# Start all services
pnpm arty up
# Stop all services
pnpm arty down
# View running containers
pnpm arty ps
# Follow logs for all services
pnpm arty logs
# Restart all services
pnpm arty restart
# Pull latest images
pnpm arty pull
# View rendered configuration (with variables substituted)
pnpm arty config
Network Setup
# Create external Docker network (required before first up)
pnpm arty net/create
Database Operations (Sexy/Directus)
# Export Directus database + schema snapshot
pnpm arty db/dump
# Import database dump + apply schema snapshot
pnpm arty db/import
# Export Directus uploads directory
pnpm arty uploads/export
# Import Directus uploads directory
pnpm arty uploads/import
Deployment
# Sync .env file to remote VPS
pnpm arty env/sync
Service-Specific Details
Core Services (core/compose.yaml)
- postgres: PostgreSQL 16 Alpine, exposed on host port 5432
- Uses scram-sha-256 authentication
- Health check via
pg_isready - Init scripts mounted from
./postgres/init/
- redis: Redis 7 Alpine for caching
- Used by Directus for cache and websocket storage
Sexy (sexy/compose.yaml)
Directus headless CMS + SvelteKit frontend:
- directus: Directus 11.12.0
- Admin panel + GraphQL/REST API
- Traefik routes
/apipath to port 8055 - Volumes:
directus_uploadsfor media,directus_bundlefor extensions - Email via SMTP (IONOS configuration in .env)
- frontend: Custom SvelteKit app from ghcr.io/valknarxxx/sexy
- Serves on port 3000
- Shared
directus_bundlevolume for Directus extensions
Proxy (proxy/compose.yaml)
Traefik 3.x reverse proxy:
- Global HTTP→HTTPS redirect
- Let's Encrypt via TLS challenge
- Dashboard disabled (
api.dashboard=false) - Reads labels from Docker socket (
/var/run/docker.sock) - Scoped to
$NETWORK_NAMEnetwork via provider configuration
AWSM (awsm/compose.yaml)
Next.js app with embedded SQLite:
- Serves awesome-app list directory
- Optional GitHub token for API rate limits
- Optional webhook secret for database updates
- Database persisted in
awesome_datavolume
Scrapy (scrapy/compose.yaml)
Web scraping cluster with three services:
- scrapyd: Scrapyd daemon exposed at
scrapy.pivoine.art:6800- Web interface for deploying and managing spiders
- Protected by HTTP Basic Auth (credentials in
.env) - Data persisted in
scrapyd_datavolume
- scrapy: Development container for running Scrapy commands
- Shared
scrapy_codevolume for spider projects
- Shared
- scrapyrt: Scrapyd Real-Time API on port 9080
- Run spiders via HTTP API without scheduling
Authentication: Access requires username/password (stored as SCRAPY_AUTH_USERS in .env using htpasswd format)
n8n (n8n/compose.yaml)
Workflow automation platform:
- n8n: n8n application exposed at
n8n.pivoine.art:5678- Visual workflow builder with 200+ integrations
- PostgreSQL backend for workflow persistence
- Runners enabled for task execution
- Webhook support for external triggers
- Data persisted in
n8n_datavolume
Stash (stash/compose.yaml)
Web-based file manager:
- filestash: Filestash app exposed at
stash.pivoine.art:8334- Support for multiple storage backends (SFTP, S3, Dropbox, Google Drive, FTP, WebDAV)
- In-browser file viewer and media player
- File sharing capabilities
- Data persisted in
filestash_datavolume
Links (links/compose.yaml)
Linkwarden bookmark manager with full-text search:
- linkwarden: Linkwarden app exposed at
links.pivoine.art:3000- Bookmark and link management with collections
- Full-text search via Meilisearch
- Collaborative bookmark sharing
- Screenshot and PDF archiving
- Browser extension support
- PostgreSQL backend for bookmark persistence
- Data persisted in
linkwarden_datavolume
- linkwarden_meilisearch: Meilisearch v1.12.8 search engine
- Powers full-text search for bookmarks
- Data persisted in
linkwarden_meili_datavolume
Required Environment Variables (add to .env):
LINKS_NEXTAUTH_SECRET: NextAuth.js secret for session encryptionLINKS_MEILI_MASTER_KEY: Meilisearch master key for API authentication
Restic (restic/compose.yaml)
Backrest backup system with restic backend:
- backrest: Backrest web UI exposed at
restic.pivoine.art:9898- Web-based interface for managing restic backups
- Automated scheduled backups with retention policies
- Support for multiple backup plans and repositories
- Real-time backup status and history
- Restore capabilities via web UI
- Data persisted in
backrest_data,backrest_config,backrest_cachevolumes
Backup Configuration:
- Backup Target:
/mnt/hidrive/users/valknar/Backup(mounted to container as/repos) - Volumes Backed Up (all mounted read-only to
/volumes/):core_postgres_data- PostgreSQL database filescore_redis_data- Redis datadirectus_uploads- Directus media filesdirectus_bundle- Directus extensionsawesome_data- AWSM SQLite databasegotify_data- Gotify notificationsscrapyd_data,scrapy_code- Scrapy spider datan8n_data- n8n workflow configurationsfilestash_data- Filestash statelinkwarden_data,linkwarden_meili_data- Linkwarden bookmarks and search indexletsencrypt_data- SSL certificates
Important: The backup destination path must be accessible from the container. For HiDrive, ensure the mount point exists on the host and is properly mounted before starting the backup service.
Important Environment Variables
Key variables defined in arty.yml and overridden in .env:
NETWORK_NAME: Docker network name (default:falcon_network)ADMIN_EMAIL: Used for Let's Encrypt and service admin accountsDB_USER,DB_PASSWORD: PostgreSQL credentialsCORE_DB_HOST,CORE_DB_PORT: PostgreSQL connection (default:postgres:5432)CORE_REDIS_HOST,CORE_REDIS_PORT: Redis connection (default:redis:6379){SERVICE}_TRAEFIK_HOST: Domain for each service{SERVICE}_TRAEFIK_ENABLED: Toggle Traefik exposureSEXY_DIRECTUS_SECRET: Directus security secretTRACK_APP_SECRET: Umami analytics secret
Volume Management
Each service uses named volumes prefixed with project name:
core_postgres_data,core_redis_data: Database persistencecore_directus_uploads,core_directus_bundle: Directus media/extensionsawesome_data: AWSM SQLite databasescrapy_scrapyd_data,scrapy_scrapy_code: Scrapy spider data and coden8n_n8n_data: n8n workflow datastash_filestash_data: Filestash configuration and statelinks_data,links_meili_data: Linkwarden bookmarks and Meilisearch indexrestic_data,restic_config,restic_cache,restic_tmp: Backrest backup systemproxy_letsencrypt_data: SSL certificates
Volumes can be inspected with:
docker volume ls | grep falcon
docker volume inspect <volume_name>
Security Configuration
HTTP Basic Authentication
Scrapyd is protected with HTTP Basic Auth via Traefik middleware:
- Credentials stored in
.envasSCRAPY_AUTH_USERS - Format:
username:$apr1$hash(Apache htpasswd format) - Generate new hash:
openssl passwd -apr1 'your_password' - Remember to escape
$signs with$$in.envfiles
To update credentials:
# Generate hash
echo "username:$(openssl passwd -apr1 'new_password')"
# Update .env
SCRAPY_AUTH_USERS=username:$$apr1$$hash$$here
# Sync to VPS
rsync -avzhe ssh .env root@vps:~/Projects/docker-compose/
# Restart services
ssh -A root@vps "cd ~/Projects/docker-compose && arty restart"
Security Headers & TLS
Global security settings applied via proxy/dynamic/security.yaml:
- TLS: Minimum TLS 1.2, strong ciphers only, SNI strict mode
- Headers: HSTS, X-Frame-Options, CSP, Referrer-Policy, etc.
- Rate Limiting: Available middlewares for DDoS protection
Test security:
# Check headers
curl -I https://scrapy.pivoine.art
# SSL Labs test
# Visit: https://www.ssllabs.com/ssltest/analyze.html?d=scrapy.pivoine.art
Modifying Security Settings
Edit proxy/dynamic/security.yaml to customize:
- TLS versions and cipher suites
- Security header values
- Rate limiting thresholds
Traefik automatically reloads changes (no restart needed).
Troubleshooting
Services won't start
- Ensure external network exists:
pnpm arty net/create - Check if services reference correct
$NETWORK_NAMEin labels - Verify
.envhas required secrets (compare witharty.ymlenvs.default)
SSL certificates failing
- Check
ADMIN_EMAILis set in.env - Ensure ports 80/443 are accessible from internet
- Inspect Traefik logs:
docker logs proxy_app
Database connection errors
- Check PostgreSQL is healthy:
docker ps(should show healthy status) - Verify database exists:
docker exec core_postgres psql -U $DB_USER -l - Check credentials match between
.envand service configs
Directus schema migration
- Export schema:
pnpm arty db/dump(createssexy/directus.yaml) - Import to new instance:
pnpm arty db/import(applies schema snapshot) - Schema file stored in
sexy/directus.yamlfor version control