From c89769a23f9ad705fd44a8cffc135a03fab5d0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Thu, 6 Nov 2025 10:13:07 +0100 Subject: [PATCH] feat: add Restic backup stack with Backrest UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CLAUDE.md | 30 ++++++++++++ README.md | 9 ++++ arty.yml | 7 +++ compose.yaml | 1 + restic/compose.yaml | 111 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 restic/compose.yaml diff --git a/CLAUDE.md b/CLAUDE.md index 37c40b2..7d81d38 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,6 +20,8 @@ Root `compose.yaml` uses Docker Compose's `include` directive to orchestrate mul - **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`). @@ -195,6 +197,33 @@ Linkwarden bookmark manager with full-text search: - `LINKS_NEXTAUTH_SECRET`: NextAuth.js secret for session encryption - `LINKS_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_cache` volumes + +**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 files + - `core_redis_data` - Redis data + - `directus_uploads` - Directus media files + - `directus_bundle` - Directus extensions + - `awesome_data` - AWSM SQLite database + - `gotify_data` - Gotify notifications + - `scrapyd_data`, `scrapy_code` - Scrapy spider data + - `n8n_data` - n8n workflow configurations + - `filestash_data` - Filestash state + - `linkwarden_data`, `linkwarden_meili_data` - Linkwarden bookmarks and search index + - `letsencrypt_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`: @@ -218,6 +247,7 @@ Each service uses named volumes prefixed with project name: - `n8n_n8n_data`: n8n workflow data - `stash_filestash_data`: Filestash configuration and state - `links_data`, `links_meili_data`: Linkwarden bookmarks and Meilisearch index +- `restic_data`, `restic_config`, `restic_cache`, `restic_tmp`: Backrest backup system - `proxy_letsencrypt_data`: SSL certificates Volumes can be inspected with: diff --git a/README.md b/README.md index baa28de..4c143c0 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ The **Falcon** is a state-of-the-art containerized starship, powered by Docker's | **N8N** | *Automated workflow command center* | [n8n.pivoine.art](https://n8n.pivoine.art) | | **STASH** | *Universal file management portal* | [stash.pivoine.art](https://stash.pivoine.art) | | **LINKS** | *Interstellar bookmark archive* | [links.pivoine.art](https://links.pivoine.art) | +| **RESTIC** | *Automated backup vault system* | [restic.pivoine.art](https://restic.pivoine.art) | | **PROXY** | *Shield control dashboard* | [proxy.pivoine.art](https://proxy.pivoine.art) | | **VPN** | *Cloaking device network* | [vpn.pivoine.art](https://vpn.pivoine.art) | @@ -74,6 +75,11 @@ The **Falcon** is a state-of-the-art containerized starship, powered by Docker's ├─────────────────────────────────────────────────┤ │ ⚡ REDIS CACHE HYPERDRIVE │ │ └─ Warp-speed data acceleration │ +├─────────────────────────────────────────────────┤ +│ 🔐 BACKREST BACKUP VAULT (Restic) │ +│ ├─ Automated volume snapshots │ +│ ├─ Incremental backup engine │ +│ └─ HiDrive remote repository │ └─────────────────────────────────────────────────┘ ``` @@ -172,6 +178,7 @@ THE FALCON (falcon_network) │ ├─ n8n Workflows [n8n.pivoine.art] │ ├─ Filestash Files [stash.pivoine.art] │ ├─ Linkwarden Marks [links.pivoine.art] +│ ├─ Backrest Backups [restic.pivoine.art] │ └─ WireGuard VPN [vpn.pivoine.art] │ └─ 💾 STORAGE VOLUMES @@ -185,6 +192,8 @@ THE FALCON (falcon_network) ├─ filestash_data → File manager state ├─ linkwarden_data → Bookmark archives ├─ meili_data → Search index database + ├─ backrest_data → Backup system state + ├─ backrest_config → Backup configurations └─ letsencrypt_data → Shield certificates ``` diff --git a/arty.yml b/arty.yml index 1b4dffd..9d31c4d 100644 --- a/arty.yml +++ b/arty.yml @@ -87,6 +87,13 @@ envs: LINKS_DB_NAME: linkwarden LINKS_MEILI_IMAGE: getmeili/meilisearch:v1.12.8 LINKS_MEILI_NO_ANALYTICS: true + # Restic + RESTIC_TRAEFIK_ENABLED: true + RESTIC_COMPOSE_PROJECT_NAME: restic + RESTIC_IMAGE: garethgeorge/backrest:latest + RESTIC_TRAEFIK_HOST: restic.pivoine.art + RESTIC_HOSTNAME: falcon + RESTIC_BACKUP_PATH: /mnt/hidrive/users/valknar/Backup # Proxy PROXY_COMPOSE_PROJECT_NAME: proxy PROXY_DOCKER_IMAGE: traefik:latest diff --git a/compose.yaml b/compose.yaml index 36a3157..8d97bdb 100644 --- a/compose.yaml +++ b/compose.yaml @@ -8,6 +8,7 @@ include: - n8n/compose.yaml - stash/compose.yaml - links/compose.yaml + - restic/compose.yaml - umami/compose.yaml - sablier/compose.yaml - proxy/compose.yaml diff --git a/restic/compose.yaml b/restic/compose.yaml new file mode 100644 index 0000000..51da1eb --- /dev/null +++ b/restic/compose.yaml @@ -0,0 +1,111 @@ +services: + backrest: + image: ${RESTIC_IMAGE:-garethgeorge/backrest:latest} + container_name: ${RESTIC_COMPOSE_PROJECT_NAME}_app + restart: unless-stopped + hostname: ${RESTIC_HOSTNAME:-falcon} + volumes: + # Backrest application data + - backrest_data:/data + - backrest_config:/config + - backrest_cache:/cache + - backrest_tmp:/tmp + + # Backup destination + - ${RESTIC_BACKUP_PATH:-/mnt/hidrive/users/valknar/Backup}:/repos + + # Docker volumes to backup (read-only) + - core_postgres_data:/volumes/core_postgres_data:ro + - core_redis_data:/volumes/core_redis_data:ro + - directus_uploads:/volumes/directus_uploads:ro + - directus_bundle:/volumes/directus_bundle:ro + - awesome_data:/volumes/awesome_data:ro + - gotify_data:/volumes/gotify_data:ro + - scrapyd_data:/volumes/scrapyd_data:ro + - scrapy_code:/volumes/scrapy_code:ro + - n8n_data:/volumes/n8n_data:ro + - filestash_data:/volumes/filestash_data:ro + - linkwarden_data:/volumes/linkwarden_data:ro + - linkwarden_meili_data:/volumes/linkwarden_meili_data:ro + - letsencrypt_data:/volumes/letsencrypt_data:ro + + environment: + TZ: ${TIMEZONE:-Europe/Berlin} + BACKREST_DATA: /data + BACKREST_CONFIG: /config/config.json + XDG_CACHE_HOME: /cache + TMPDIR: /tmp + + networks: + - compose_network + + labels: + - 'traefik.enable=${RESTIC_TRAEFIK_ENABLED}' + - 'traefik.http.middlewares.${RESTIC_COMPOSE_PROJECT_NAME}-redirect-web-secure.redirectscheme.scheme=https' + - 'traefik.http.routers.${RESTIC_COMPOSE_PROJECT_NAME}-web.middlewares=${RESTIC_COMPOSE_PROJECT_NAME}-redirect-web-secure' + - 'traefik.http.routers.${RESTIC_COMPOSE_PROJECT_NAME}-web.rule=Host(`${RESTIC_TRAEFIK_HOST}`)' + - 'traefik.http.routers.${RESTIC_COMPOSE_PROJECT_NAME}-web.entrypoints=web' + - 'traefik.http.routers.${RESTIC_COMPOSE_PROJECT_NAME}-web-secure.rule=Host(`${RESTIC_TRAEFIK_HOST}`)' + - 'traefik.http.routers.${RESTIC_COMPOSE_PROJECT_NAME}-web-secure.tls.certresolver=resolver' + - 'traefik.http.routers.${RESTIC_COMPOSE_PROJECT_NAME}-web-secure.entrypoints=web-secure' + - 'traefik.http.middlewares.${RESTIC_COMPOSE_PROJECT_NAME}-web-secure-compress.compress=true' + - 'traefik.http.routers.${RESTIC_COMPOSE_PROJECT_NAME}-web-secure.middlewares=${RESTIC_COMPOSE_PROJECT_NAME}-web-secure-compress' + - 'traefik.http.services.${RESTIC_COMPOSE_PROJECT_NAME}-web-secure.loadbalancer.server.port=9898' + - 'traefik.docker.network=${NETWORK_NAME}' + - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' + +volumes: + backrest_data: + name: ${RESTIC_COMPOSE_PROJECT_NAME}_data + backrest_config: + name: ${RESTIC_COMPOSE_PROJECT_NAME}_config + backrest_cache: + name: ${RESTIC_COMPOSE_PROJECT_NAME}_cache + backrest_tmp: + name: ${RESTIC_COMPOSE_PROJECT_NAME}_tmp + + # External volumes from other stacks (read-only mounts) + core_postgres_data: + name: core_postgres_data + external: true + core_redis_data: + name: core_redis_data + external: true + directus_uploads: + name: core_directus_uploads + external: true + directus_bundle: + name: core_directus_bundle + external: true + awesome_data: + name: awesome_data + external: true + gotify_data: + name: messaging_data + external: true + scrapyd_data: + name: scrapy_scrapyd_data + external: true + scrapy_code: + name: scrapy_scrapy_code + external: true + n8n_data: + name: n8n_n8n_data + external: true + filestash_data: + name: stash_filestash_data + external: true + linkwarden_data: + name: links_data + external: true + linkwarden_meili_data: + name: links_meili_data + external: true + letsencrypt_data: + name: proxy_letsencrypt_data + external: true + +networks: + compose_network: + name: ${NETWORK_NAME} + external: true