feat(backups): añadir stack de backups cifrados con Kopia + Cloudflare R2
- init-r2-repository.sh: inicializa repositorio Kopia en R2 - run-backup.sh: ejecuta backup de rutas definidas en backup-paths.txt - pre-backup-dumps.sh: vuelca bases de datos (Postgres, MariaDB) antes del backup - kopia-common.sh: funciones compartidas de autenticación y configuración - restore-smoke-test.sh: test de restauración para verificar integridad - backup-paths.txt / excludes.txt: rutas y exclusiones del backup - systemd/: unidades kopia-server, kopia-docker-backup (service + timer) - traefik-kopia.yml: exposición del servidor Kopia vía Traefik - env.example: plantilla de variables (credenciales R2, contraseña repo) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# Kopia backups to Cloudflare R2
|
||||
|
||||
This directory defines restore-focused backups for the Docker services that are actually running on the host. It uses Kopia with Cloudflare R2 as an S3-compatible repository.
|
||||
|
||||
## Policy
|
||||
|
||||
- Use Kopia as the only backup engine.
|
||||
- Store the repository in Cloudflare R2.
|
||||
- Keep secrets outside this repo in `/opt/kopia/kopia.env`.
|
||||
- Snapshot data needed to restore services and configuration.
|
||||
- Generate logical database dumps before each snapshot.
|
||||
- Include Paperless documents and Gitea repositories.
|
||||
- Exclude Nextcloud user data/multimedia until storage usage and R2 cost are explicitly accepted.
|
||||
|
||||
Current size check on 2026-04-27:
|
||||
|
||||
| Path | Size |
|
||||
| --- | ---: |
|
||||
| `/opt/paperless` | 316M |
|
||||
| `/opt/gitea` | 6.0M |
|
||||
| `/opt/nextcloud` | 2.8G |
|
||||
| `/opt/onlyoffice` | 4.3M |
|
||||
| `/opt/authentik` | 8.3M |
|
||||
| `/opt/trilium` | 3.6M |
|
||||
| `/opt/karakeep` | 5.2M |
|
||||
| `/opt/spintools` | 335M |
|
||||
|
||||
## What is backed up
|
||||
|
||||
The snapshot paths are listed in `backup-paths.txt`. The important choices are:
|
||||
|
||||
- Paperless: `/opt/paperless/data`, `/opt/paperless/media`, export and consume directories.
|
||||
- Gitea: `/opt/gitea/data` and runner data.
|
||||
- Nextcloud: config, themes and a MariaDB dump only. `/opt/nextcloud/data`, `/opt/nextcloud/custom_apps` and `/opt/nextcloud/html` are excluded.
|
||||
- Databases: PostgreSQL and MariaDB dumps are written to `/opt/kopia/staging/dumps/latest`.
|
||||
- Service configuration: deployment repo, Traefik, Authentik, n8n, Memos, Vikunja, Trilium, Karakeep, AdGuard, WireGuard, mail relay, Beszel, Homepage, Dozzle, Kasm and OpenCode.
|
||||
- Local active deployments: Scheduler, Spintools, THSCOM, OpenClaw restore state and OpenCode runtime state.
|
||||
|
||||
Live database directories are excluded when a logical dump exists. Kasm's PostgreSQL database is backed up with `pg_dump`; Docker image/layer storage under `/opt/kasm/opt/docker` is intentionally excluded because it is rebuildable and large. OpenProject's embedded PostgreSQL data is not dumped by this first version; treat its restore as best-effort unless a service-specific dump command is added.
|
||||
|
||||
## Install Kopia
|
||||
|
||||
Install Kopia on the host using the package method appropriate for the server. Then create the runtime directory:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/kopia
|
||||
sudo cp backups/kopia/env.example /opt/kopia/kopia.env
|
||||
sudo chmod 600 /opt/kopia/kopia.env
|
||||
```
|
||||
|
||||
Edit `/opt/kopia/kopia.env` and set the R2 bucket, endpoint, access keys and `KOPIA_PASSWORD`.
|
||||
|
||||
Cloudflare R2 endpoint format:
|
||||
|
||||
```text
|
||||
<account_id>.r2.cloudflarestorage.com
|
||||
```
|
||||
|
||||
## Initialize
|
||||
|
||||
```bash
|
||||
backups/kopia/init-r2-repository.sh
|
||||
```
|
||||
|
||||
## Run manually
|
||||
|
||||
```bash
|
||||
backups/kopia/run-backup.sh
|
||||
```
|
||||
|
||||
The script:
|
||||
|
||||
1. Creates database dumps.
|
||||
2. Snapshots the paths in `backup-paths.txt`.
|
||||
3. Applies retention: latest 7, daily 7, weekly 4, monthly 6.
|
||||
4. Runs `kopia repository validate-provider`.
|
||||
|
||||
## Enable timer
|
||||
|
||||
Install the scripts under `/opt/kopia/scripts`; systemd should not execute them directly from a home directory on SELinux-enforcing hosts.
|
||||
|
||||
```bash
|
||||
sudo install -d -m 0755 /opt/kopia/scripts
|
||||
sudo install -m 0755 backups/kopia/*.sh /opt/kopia/scripts/
|
||||
sudo install -m 0644 backups/kopia/backup-paths.txt backups/kopia/excludes.txt /opt/kopia/scripts/
|
||||
sudo cp backups/kopia/systemd/kopia-docker-backup.* /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now kopia-docker-backup.timer
|
||||
```
|
||||
|
||||
## Web UI
|
||||
|
||||
Kopia UI is exposed at:
|
||||
|
||||
```text
|
||||
https://kopia.sherlockhomeless.net
|
||||
```
|
||||
|
||||
The service listens only on the Docker host gateway `10.0.0.1:51515` and is published through Traefik using `traefik-kopia.yml`.
|
||||
|
||||
Credentials are stored outside the repo in `/opt/kopia/kopia.env`:
|
||||
|
||||
```bash
|
||||
sudo awk -F= '/^KOPIA_SERVER_USERNAME|^KOPIA_SERVER_PASSWORD/ {print}' /opt/kopia/kopia.env
|
||||
```
|
||||
|
||||
Install or update the service and Traefik route:
|
||||
|
||||
```bash
|
||||
sudo cp backups/kopia/systemd/kopia-server.service /etc/systemd/system/
|
||||
sudo cp backups/kopia/traefik-kopia.yml /opt/traefik/dynamic/kopia.yml
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now kopia-server.service
|
||||
```
|
||||
|
||||
## Restore smoke test
|
||||
|
||||
Requires `jq`.
|
||||
|
||||
```bash
|
||||
backups/kopia/restore-smoke-test.sh /tmp/kopia-restore-test
|
||||
```
|
||||
|
||||
For real restores, restore into a temporary directory first, stop the target service, move data into place with correct ownership, then start the stack and validate the application.
|
||||
Reference in New Issue
Block a user