diff --git a/nas/garage/README.md b/nas/garage/README.md new file mode 100644 index 0000000..e2346d8 --- /dev/null +++ b/nas/garage/README.md @@ -0,0 +1,35 @@ +# Garage on the NAS + +S3 storage for Velero, on the Synology (192.168.1.99) in Docker, outside +the cluster. Replaced MinIO, which is no longer maintained. One node, data +in `/volume1/docker/garage/{meta,data}`. S3 API on port 3900, admin API on +3903, both LAN only. Set up 2026-10-25 with Garage v2.5.0. + +The NAS is reached as `ssh -p 34000 rubenhensen@192.168.1.99`; docker is +in `/usr/local/bin`, which a non-interactive SSH session doesn't have on +its PATH. + +## Setup + +`garage.toml` here has placeholders for the two secrets; on the NAS they +were filled in once with `openssl rand -hex 32` (`rpc_secret`) and +`openssl rand -base64 32` (`admin_token`), and only exist there. + +``` +cd /volume1/docker/garage +docker compose up -d +g="docker exec garage /garage" +$g layout assign -z nas -c 800G +$g layout apply --version 1 +$g bucket create velero-backups +$g key create velero +$g bucket allow --read --write --key velero velero-backups +``` + +Velero's key is in Vault as `kv/velero-s3` (`access_key_id`, +`secret_access_key`) and reaches Velero through +`velero/external-secret.yaml`. Without the cluster, `$g key info velero +--show-secret` on the NAS shows it again. + +Renovate updates the image tag in `compose.yaml`, but nothing deploys it: +after merging, `docker compose pull && docker compose up -d` on the NAS. diff --git a/nas/garage/compose.yaml b/nas/garage/compose.yaml new file mode 100644 index 0000000..31ea528 --- /dev/null +++ b/nas/garage/compose.yaml @@ -0,0 +1,12 @@ +services: + garage: + image: dxflrs/garage:v2.5.0 + container_name: garage + restart: unless-stopped + ports: + - "3900:3900" # S3 API + - "3903:3903" # admin API + volumes: + - ./garage.toml:/etc/garage.toml:ro + - ./meta:/var/lib/garage/meta + - ./data:/var/lib/garage/data diff --git a/nas/garage/garage.toml b/nas/garage/garage.toml new file mode 100644 index 0000000..38b2795 --- /dev/null +++ b/nas/garage/garage.toml @@ -0,0 +1,29 @@ +# One node on the Synology. Velero's kopia data is encrypted, so +# compression would only cost CPU. +# +# SQLite rather than the default LMDB: LMDB can be corrupted by an unclean +# shutdown, and a single node has no other node to repair from. With +# metadata_fsync SQLite runs WAL with synchronous=NORMAL, which only syncs +# at checkpoints; LMDB with metadata_fsync synced every transaction, ~270 +# writes/s on the NAS's two disks with Garage idle. Converted 2026-10-25 +# with `garage convert-db`. +metadata_dir = "/var/lib/garage/meta" +data_dir = "/var/lib/garage/data" +db_engine = "sqlite" +metadata_fsync = true +data_fsync = true +metadata_auto_snapshot_interval = "6h" +replication_factor = 1 +compression_level = "none" + +rpc_bind_addr = "[::]:3901" +rpc_public_addr = "127.0.0.1:3901" +rpc_secret = "@RPC_SECRET@" + +[s3_api] +s3_region = "garage" +api_bind_addr = "[::]:3900" + +[admin] +api_bind_addr = "[::]:3903" +admin_token = "@ADMIN_TOKEN@" diff --git a/velero/external-secret.yaml b/velero/external-secret.yaml new file mode 100644 index 0000000..7356295 --- /dev/null +++ b/velero/external-secret.yaml @@ -0,0 +1,30 @@ +# Velero's key for its bucket in Garage on the NAS (nas/garage), in the +# AWS credentials file format the aws plugin and kopia read. +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: velero-s3-credentials +spec: + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + refreshInterval: 15m + target: + name: velero-s3-credentials + creationPolicy: Owner + template: + engineVersion: v2 + data: + cloud: | + [default] + aws_access_key_id={{ .access_key_id }} + aws_secret_access_key={{ .secret_access_key }} + data: + - secretKey: access_key_id + remoteRef: + key: kv/velero-s3 + property: access_key_id + - secretKey: secret_access_key + remoteRef: + key: kv/velero-s3 + property: secret_access_key