All apps · 0 apps
GarageWare
Docker app from make-ware's Repository
Overview
Readme
View on GitHubgarage-ware
Self-hosted management console for a Garage HQ S3-compatible
storage cluster. One Docker image, one /data volume to back up. It manages a cluster you already
run — it does not install one.
Users get self-service S3 access keys, buckets and quotas; admins get cluster status, storage grants, node repairs and a cluster event timeline. Next.js 16 + React 19 over PocketBase (identity, key/bucket mappings, storage claims) and the Garage admin API, proxied server-side so its token never reaches the browser.
Repo: make-ware/garage-ware · Images:
dastron/garage-ware (Docker Hub) and ghcr.io/make-ware/garage-ware (GHCR) — both public, same
digests, either works.
Quick start
You need a running Garage cluster with a layout applied and an admin token.
With Docker Compose — the recommended path:
git clone https://github.com/make-ware/garage-ware.git
cd garage-ware
cp .env.docker.example .env.docker # set GARAGE_ADMIN_URL, GARAGE_ADMIN_TOKEN, GARAGE_S3_ENDPOINT
docker compose up -d
docker compose logs garage-ware | grep '\[setup\]'
With plain Docker (docker run) — no checkout needed: download
.env.docker.example, save it as .env.docker (that filename, not .env)
and fill in the same three values.
docker run -d \
--name garage-ware \
--restart unless-stopped \
-p 8080:80 \
-v $PWD/data:/data \
--env-file .env.docker \
dastron/garage-ware:latest # or ghcr.io/make-ware/garage-ware:latest
docker logs garage-ware | grep '\[setup\]'
The health check ships in the image, so both paths get it.
The banner prints a URL and a one-time claim token. Open the URL, create your account, paste the
token — you are now the administrator, and land on /admin/status.
/admin/status is the answer to "why isn't this working?" It names anything still
misconfigured and the fix for each, and when it cannot reach a cluster at all it links to Garage's
install docs and to the admin-only
config generator, which writes a garage.toml
for you to place on each node without ever handling a secret.
Pin a release with dastron/garage-ware:v1.8.3 instead of tracking latest; to build from your
checkout, uncomment the build: block in docker-compose.yml.
docker/README.md has the full environment reference, SMTP and bucket-CORS
setup, and a troubleshooting table.
Admin token
Create one on any Garage node:
garage admin-token create --name garage-ware
For least-privilege scope and hardening, see docs/ARCHITECTURE.md#admin-token-scope.
Before you invite anyone
- Sign-up is closed by default (
SIGNUP_MODE); it stays open only while no administrator exists, which is why claiming promptly matters. - Configure email at
/_/#/settings/mail, or invites and password resets silently never arrive. - A new user has no storage until you grant a claim from Admin → Claims.
- Self-service claiming is off until
FEATURE_NODE_CLAIMS/FEATURE_ASSET_CLAIMSare set; until then an admin assigns node owners and imports existing keys and buckets.
Backup
Everything that persists lives under /data — database, uploads, generated superuser password,
first-run state. Snapshot it with tar czf backup.tgz -C $PWD data; restore by extracting on the
new host and bringing the container up against the same path.
Develop locally
yarn install && yarn setup # node 20+, yarn 4 via corepack; downloads the PocketBase binary
yarn dev # webapp :3000, PocketBase :8090, shared in watch mode
CONTRIBUTING.md covers scripts, adding a collection and the CI order; docs/ARCHITECTURE.md covers why the boundaries are where they are.
Releases
Conventional commits on main drive release-please; merging
its release PR tags vX.Y.Z and calls docker-build.yml. That
one build pushes each arch by digest to both registries, then assembles a manifest list per
registry, so Docker Hub (dastron/garage-ware) and GHCR (ghcr.io/make-ware/garage-ware) serve
byte-identical images under matching tags. Pushing to Docker Hub needs the DOCKERHUB_USERNAME and
DOCKERHUB_TOKEN repo secrets; GHCR uses the built-in GITHUB_TOKEN. A fork must edit the one
hardcoded DOCKERHUB_IMAGE line — a Docker Hub namespace does not follow the git repo.
License
MIT — see LICENSE.
Media gallery
1 / 4Install GarageWare on Unraid in a few clicks.
Find GarageWare in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Requirements
Download Statistics
Related apps
Explore more like this
Explore allDetails
dastron/garage-ware:latestRuntime arguments
- Web UI
http://[IP]:[PORT:80]/- Network
bridge- Shell
sh- Privileged
- false
Template configuration
Host port for the web app, PocketBase API and admin (proxied by nginx on container port 80).
- Target
- 80
- Default
- 8890
- Value
- 8890
Persistent storage: PocketBase database, uploads, the generated superuser credentials, and first-run state.
- Target
- /data
- Default
- /mnt/user/appdata/garage-ware
- Value
- /mnt/user/appdata/garage-ware
Base URL of your Garage cluster's ADMIN API - port 3903 by default, NOT the S3 port 3900. Example: http://YOUR-GARAGE-IP:3903. Without it there is no cluster status, no buckets, no keys and no metrics.
- Target
- GARAGE_ADMIN_URL
Admin API token for the cluster. Mint one on a Garage node with: garage admin-token create --name garage-ware
- Target
- GARAGE_ADMIN_TOKEN
Your Garage S3 gateway URL - example: https://s3.example.com or http://YOUR-GARAGE-IP:3900. Required for the in-app file browser and the bucket connect page. The browser signs S3 requests against this endpoint directly, so each bucket must allow CORS from this app's origin (see the project README).
- Target
- GARAGE_S3_ENDPOINT
How users actually reach this container, including the WebUI Port - example: http://YOUR-UNRAID-IP:8890 or https://garage.example.com. Used to build links in storage invites, usage alerts and the setup banner. If unset, those emails are skipped entirely - the invite row is still written, but nobody is told.
- Target
- APP_PUBLIC_URL
Who may create an account: 'closed' (admins create every account), 'invite' (only addresses holding a pending storage or admin invite), or 'open' (anyone who can reach the sign-up page). While no administrator exists yet sign-up stays open regardless, so you can create the account you will claim with - claim promptly.
- Target
- SIGNUP_MODE
- Default
- closed
- Value
- closed
S3 region name your Garage cluster is configured with.
- Target
- GARAGE_S3_REGION
- Default
- us-east-1
- Value
- us-east-1
Optional. The S3 URL advertised to users, when it differs from the CORS-enabled gateway the in-app browser must talk to. Falls back to Garage S3 Endpoint.
- Target
- GARAGE_PUBLIC_S3_ENDPOINT
Optional. Set to 'true' to let users claim cluster nodes (by proving the full node id) and grant storage sourced from them. Off by default - admins assign owners and grant all storage. Ownership decides who may grant, never how much.
- Target
- FEATURE_NODE_CLAIMS
Optional. Set to 'true' to let users self-claim pre-existing Garage keys (by proving the secret) and the buckets those keys own. Off by default - admins onboard assets via import instead.
- Target
- FEATURE_ASSET_CLAIMS
Optional. Supply the one-time first-run claim token yourself instead of letting the container mint one and print it to the log. Never written to disk.
- Target
- SETUP_CLAIM_TOKEN
Optional and WEAKER than the claim token: auto-promotes whoever signs up with this address, but only while no administrator exists yet. An address is rarely secret and sign-up is open until the instance is claimed, so anyone who reaches the app first and guesses it becomes the administrator. Prefer the token unless this container is unreachable from the internet.
- Target
- SETUP_OWNER_EMAIL
Optional. Email for the PocketBase superuser. Leave BOTH this and Admin Password blank and the container generates a password on first boot, storing it at /data/pb_superuser.env (mode 0600) - back that file up. Setting only one of the two is a hard error.
- Target
- POCKETBASE_ADMIN_EMAIL
Optional. Password for the PocketBase superuser. Set this only together with Admin Email - setting one without the other is a hard error and the container will not start.
- Target
- POCKETBASE_ADMIN_PASSWORD
Optional. Average object size in MB. When set, each bucket also gets an object-count cap derived from its byte quota. Leave blank to apply no object cap.
- Target
- GARAGE_AVG_OBJECT_SIZE_MB
How many days of per-node metrics history to keep. 0 keeps everything forever.
- Target
- NODE_METRICS_RETENTION_DAYS
- Default
- 90
- Value
- 90