- Shell 86.1%
- Nix 13.9%
|
All checks were successful
Deploy storage stack / deploy (push) Successful in 30s
# Conflicts: # .forgejo/workflows/deploy.yaml |
||
|---|---|---|
| .forgejo/workflows | ||
| init | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| flake.lock | ||
| flake.nix | ||
| garage.toml | ||
| README.md | ||
| renovate.json | ||
| 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) or asxpio's key, which may put and delete there for its
admin's file uploads.
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.