- Shell 86.1%
- Nix 13.9%
|
All checks were successful
Deploy storage stack / deploy (push) Successful in 20s
The MinIO root account is gone and the public bucket's uploader was missing. Assisted-by: Claude:fable-5-2 |
||
|---|---|---|
| .forgejo/workflows | ||
| init | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| flake.lock | ||
| flake.nix | ||
| garage.toml | ||
| README.md | ||
| SECRETS.md | ||
storage
Postgres + Garage (S3) for asxp.io and any future small services that need a database or object storage. Single-host, no clustering. Deploys via Forgejo Actions to the same box that runs the apps.
What's here
docker-compose.yml—postgres, its one-shot init container, andgarage.garage.toml— Garage's config (single node, no secrets in it).init/postgres-init.sh— idempotent role/DB bootstrap.init/garage-bootstrap.sh— idempotent buckets, per-app keys and grants; run on the host by the deploy, throughdocker exec..forgejo/workflows/deploy.yaml— pushes compose +.envto the prod host and runsdocker compose up -d.
Networking
Two Docker networks:
storage— internal-only; apps join this as an external network to reachpostgres:5432andgarage:3900.traefik— existing reverse-proxy network. Only Garage joins it, to expose the S3 API athttps://s3.asxp.io.
Garage replaced MinIO in November 2026 (MinIO was abandoned upstream and its images removed from the registries).
The public bucket
Garage answers no anonymous S3 request. public is marked as a website
instead, and Traefik sends unsigned GET/HEAD of
https://s3.asxp.io/public/<key> to Garage's web endpoint. Anyone who knows a
key can read it; nobody can list the bucket. Uploading needs the uploader key
(see SECRETS.md).
Looking inside
The image has no shell; use the CLI through docker exec:
ssh prod 'docker exec storage-garage /garage status'
ssh prod 'docker exec storage-garage /garage bucket list'
ssh prod 'docker exec storage-garage /garage key list'
Adding a new app
- Pick an app name, e.g.
foo. - Pick a DB password, S3 access key, S3 secret key for it (any random hex).
- Add Forgejo secrets
FOO_DB_PASSWORD,FOO_S3_ACCESS_KEY,FOO_S3_SECRET_KEYto this repo and to the foo repo. - Extend
init/postgres-init.shwithensure_role "foo" ...andensure_db "foo" "foo". - Extend
init/garage-bootstrap.shwithensure_bucket "foo-...", anensure_key foo ...and agrantfor it. - Pass the new envs through in
docker-compose.ymland the deploy workflow. - Push.
State and backups
Three named Docker volumes are stateful:
pg_data(Postgres data dir) —/var/lib/docker/volumes/storage_pg_data/_datagarage_data(S3 object bytes) andgarage_meta(Garage's database, with a snapshot every 6 h) —/var/lib/docker/volumes/storage_garage_*/_data
/opt/storage/secrets/garage_rpc_secret (Garage's node-to-node and CLI
secret) is made on the host at the first deploy and is not in Forgejo.
Server-level backup covers all three. If you ever move off whole-server backup,
the minimum is pg_dumpall for Postgres and rclone sync of each bucket.
The old storage_minio_data volume is still on the host from before the move;
nothing uses it.
Secrets
See SECRETS.md for the full list of Forgejo secrets this repo
needs. Apps that consume storage (asxpio, future) also need the per-app
credentials added on their repo so they can connect.