# Garage Ansible role to deploy [Garage](https://garagehq.deuxfleurs.fr/) S3-compatible object storage via Docker Compose, with declarative key/bucket provisioning and an optional WebUI behind htpasswd or authentik ForwardAuth. ## Requirements - Docker and Docker Compose installed on the target host - Ansible collection: `community.docker` - `htpasswd` (from `apache2-utils` / `httpd-tools`) when the WebUI is enabled and authentik ForwardAuth is *not* used - Traefik with a shared `garage_traefik_network` (default `proxy`) ## Role variables Full spec with types and defaults: `meta/argument_specs.yml`. The most common overrides: ### Service - `garage_s3_domains`: FQDNs the S3 router accepts. The first entry is the canonical hostname; `garage.toml` derives the virtual-hosted-style S3 `root_domain` from it as `.s3.` (so buckets resolve under `.s3.`). - `garage_web_domain`, `garage_webui_domain`: separate hostnames for the S3-website endpoint and the console. - `garage_image`, `garage_replication_factor`, `garage_db_engine`, `garage_s3_region`. ### Required secrets Generate with `openssl rand -hex 32` (32 bytes / 64 hex chars): - `garage_rpc_secret`: node-to-node RPC secret - `garage_admin_token`: admin API token - `garage_metrics_token`: metrics endpoint token ### WebUI authentication Three modes: 1. **htpasswd** (default): `garage_webui_username` / `garage_webui_password` in plaintext. The role hashes the password with `htpasswd -nbBC 10`, persists the hash on disk, and re-verifies with `htpasswd -vbB` so unchanged passwords don't churn the play. 2. **authentik ForwardAuth**: set `garage_webui_authentik_forward_auth: true` and `garage_webui_authentik_forward_auth_url: "https://auth.example.com/outpost.goauthentik.io/auth/traefik"`. `AUTH_USER_PASS` is dropped from the container env so authentik is the only gate. 3. **Disabled**: `garage_webui_enabled: false`. ### Layout bootstrap Setting `garage_bootstrap_enabled: true` runs the bootstrap task, which joins the local node to the layout (`zone: garage_bootstrap_zone`, capacity: `garage_bootstrap_capacity`) on the first run. The check tolerates the 16-char truncation that `garage layout show` performs. ### Declarative S3 keys and buckets ```yaml garage_s3_keys: - name: nextcloud buckets: - name: nextcloud-data permissions: [read, write] - name: backup buckets: - name: restic-prod permissions: [read, write, owner] ``` The role: - Lists existing keys (`garage key list`), creates missing ones - Lists existing buckets (`garage bucket list`), creates missing ones - Reads current permissions via `garage bucket info` and runs `garage bucket allow` only when the current RWO flags for the key don't already match the desired permissions `stdout` parsing is hardened against ANSI escapes and interleaved INFO log lines, so probe noise no longer produces spurious changes. ## Dependencies - Traefik network (`garage_traefik_network`, default `proxy`) - Internal network (`garage_internal_network`, default `internal`) ## Example playbook ```yaml - hosts: storage_servers roles: - role: digitalboard.core.garage vars: garage_s3_domains: - "storage.example.com" - "storage.int.example.com" garage_rpc_secret: "{{ vault_garage_rpc_secret }}" garage_admin_token: "{{ vault_garage_admin_token }}" garage_metrics_token: "{{ vault_garage_metrics_token }}" garage_bootstrap_enabled: true garage_webui_authentik_forward_auth: true garage_webui_authentik_forward_auth_url: "https://auth.example.com/outpost.goauthentik.io/auth/traefik" garage_s3_keys: - name: nextcloud buckets: - name: nextcloud-data permissions: [read, write] ``` ## License MIT-0