Self-hosting Garage for object storage
26 Oct 2025
Due to the work on Shubox block storage had become an interesting little corner that I’ve enjoyed researching. I had previously played with minio and found it to be fine. Recently, the maintainers of Minio had decided to change directions in how “open” they wanted to make their “open source” product. That’s fine. With the velocity and severity of these changes, though, it made sense to take a look at something that doesn’t risk having an even more severe rug-pull moment. Hence - garage.
Up and running with Garage
Docker compose is my preferred method for spinning up services in-house. For this experiment I decided to try both Garage and garage-webui together. Keep in mind that this is a single node installation, no replication. Just one machine, one instance.
That said, here’s a simple config in a compose.yml file to get things going:
services:
garage:
image: dxflrs/garage:v2.1.0
container_name: garage
volumes:
- ./garage.toml:/etc/garage.toml
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data
restart: unless-stopped
ports:
- 3900:3900
- 3901:3901
- 3902:3902
- 3903:3903
garage-webui:
image: khairul169/garage-webui:1.1.0
container_name: garage-webui
restart: unless-stopped
volumes:
- ./garage.toml:/etc/garage.toml:ro
ports:
- 3909:3909
environment:
API_BASE_URL: "http://garage:3903"
S3_ENDPOINT_URL: "http://garage:3900"
depends_on:
- garage
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3909 || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
Where the contents of ./garage.toml are:
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
replication_factor = 1
rpc_bind_addr = "[::]:3901"
rpc_public_addr = "[::]:3901"
rpc_secret = "..." # generate secret with `openssl rand -hex 32`
bootstrap_peers = []
[s3_api]
s3_region = "bully"
api_bind_addr = "[::]:3900"
root_domain = ".s3.your-domain.com"
[s3_web]
bind_addr = "[::]:3902"
root_domain = ".web.your-domain.com"
index = "index.html"
[admin]
api_bind_addr = "[::]:3903"
admin_token = "..." # generate secret with `openssl rand -hex 32`
metrics_token = "..." # generate secret with `openssl rand -hex 32`
A quick docker compose up -d will get that up and running.
Create a cluster layout
While it may be up and running, you will still need to allocate the disk space (layout) for Garage. Here’s how:
# get the node id
NODE_ID=`xxd -p ./meta/node_key.pub | tr -d '\n'`
# shorthand for the garage cli
alias garage='docker exec garage /garage -c /etc/garage.toml --rpc-host $NODE_ID@127.0.0.1:3901'
# check status
garage status
# assign node and cluster layout with zone name (dc1) and disk space (10G)
garage layout assign $NODE_ID -z dc1 -c 10G
# apply the layout
garage layout apply --version 1
Check your work
At this point you should be able to visit http://[host-ip]:3909, see Garage-webui, create buckets & keys, etc.
Beyond that, you should install an s3 client like awscli, mc, or s5cmd to test your buckets with whatever key and secret you’ve created via garage-webui.
Enjoy! 🛠️️