MinIO discontinued their Docker images and moved to a source-only distribution model, leaving many homelab installs running outdated versions with known CVEs. Garage is a self-hosted, S3-compatible distributed object storage service — lightweight, actively maintained, and a clean drop-in replacement for Longhorn backups.
Note
For restoring from Longhorn backups after this migration, see Restoring from Longhorn Backups. The process is identical regardless of whether you’re using MinIO or Garage.
Prerequisites: Ubuntu server with Docker and Docker Compose, Kubernetes cluster with Longhorn installed.
Setting Up Garage
Directory and Secrets
sudo mkdir -p /srv/docker/garage/{meta,data}cd /srv/docker/garage
# Generate RPC secretopenssl rand -hex 32
# Generate admin tokenopenssl rand -hex 32/srv/docker/garage/garage.toml
metadata_dir = "/var/lib/garage/meta"data_dir = "/var/lib/garage/data"db_engine = "lmdb"
replication_factor = 1
rpc_bind_addr = "0.0.0.0:3901"rpc_public_addr = "127.0.0.1:3901"rpc_secret = "YOUR_RPC_SECRET_HERE"
[s3_api]s3_region = "us-east-1"api_bind_addr = "0.0.0.0:3900"root_domain = ".s3.garage"
[admin]api_bind_addr = "0.0.0.0:3903"admin_token = "YOUR_ADMIN_TOKEN_HERE"Tip
replication_factor = 1 is fine for single-node. Use 3 for multi-node redundancy.
/srv/docker/garage/docker-compose.yml
version: "3"services: garage: image: dxflrs/garage:v2.1.0 container_name: garage network_mode: "host" restart: unless-stopped volumes: - ./garage.toml:/etc/garage.toml - ./meta:/var/lib/garage/meta - ./data:/var/lib/garage/data
webui: image: khairul169/garage-webui:latest container_name: garage-webui restart: unless-stopped volumes: - ./garage.toml:/etc/garage.toml:ro environment: API_BASE_URL: "http://127.0.0.1:3903" S3_ENDPOINT_URL: "http://127.0.0.1:3900" network_mode: "host"docker-compose up -dConfigure Storage
alias garage="docker exec -ti garage /garage"
garage node id
# Assign storage capacitygarage layout assign <node-id> -z default -c 100G
garage layout showgarage layout apply --version 1
# Create bucket and keygarage bucket create longhorngarage key create longhorn-keygarage bucket allow longhorn --read --write --owner --key longhorn-key
# Note the Key ID and Secret keygarage key info longhorn-key --show-secretIntegrate with Longhorn
Kubernetes Secret
apiVersion: v1kind: Secretmetadata: name: minio-secret namespace: longhorn-systemtype: OpaquestringData: AWS_ACCESS_KEY_ID: <Key-ID-from-garage> AWS_SECRET_ACCESS_KEY: <Secret-Key-from-garage> AWS_ENDPOINTS: http://<GARAGE_SERVER_IP>:3900 AWS_REGION: us-east-1kubectl apply -f minio-secret.yamlHelmRelease Update
---apiVersion: source.toolkit.fluxcd.io/v1kind: HelmRepositorymetadata: name: longhorn namespace: longhorn-systemspec: interval: 1h url: https://charts.longhorn.io---apiVersion: helm.toolkit.fluxcd.io/v2kind: HelmReleasemetadata: name: longhornspec: interval: 1h chart: spec: chart: longhorn version: 1.10.0 sourceRef: kind: HelmRepository name: longhorn namespace: longhorn-system values: defaultSettings: backupTarget: "s3://longhorn@us-east-1/" backupTargetCredentialSecret: "minio-secret" backupstorePollInterval: "300"Warning
In Longhorn 1.10.0+, backup settings are under defaultSettings, not a separate defaultBackupStore section.
Verify
Access the Garage WebUI at http://<SERVER_IP>:3909. Then test a backup from Longhorn UI → Volume → Create Backup and confirm it appears in the longhorn bucket.
garage bucket listgarage bucket info longhornTroubleshooting
WebUI “Unknown Error” — confirm the [admin] section is in garage.toml and restart:
docker-compose restartdocker logs garagePort 3903 connection refused:
netstat -tlnp | grep 3903Longhorn can’t reach Garage:
kubectl run -it --rm debug --image=amazon/aws-cli --restart=Never -- \ s3 ls s3://longhorn --endpoint-url http://<GARAGE_IP>:3900