Documentation Index
Fetch the complete documentation index at: https://mintlify.com/deuxfleurs-org/garage/llms.txt
Use this file to discover all available pages before exploring further.
The main reason to add a reverse proxy in front of Garage is to provide TLS to your users and serve multiple web services on port 443.
In production you will likely need your certificates signed by a certificate authority. The most automated way is to use a provider supporting the ACME protocol such as Let’s Encrypt or ZeroSSL.
Testing with Self-Signed Certificates
If you are only testing Garage, you can generate a self-signed certificate:
openssl req \
-new \
-x509 \
-keyout /tmp/garage.key \
-out /tmp/garage.crt \
-nodes \
-subj "/C=XX/ST=XX/L=XX/O=XX/OU=XX/CN=localhost/emailAddress=X@X.XX" \
-addext "subjectAltName = DNS:localhost, IP:127.0.0.1"
cat /tmp/garage.key /tmp/garage.crt > /tmp/garage.pem
When using self-signed certificates, you will need to allow them in your S3 client. For example, with MinIO client:
Nginx
Nginx is a well-known reverse proxy suitable for production. The configuration involves defining upstream blocks (backends) and server blocks (frontends) for both the S3 and web endpoints.
S3 API Endpoint
First, define the upstream to access your Garage cluster with load balancing:
upstream s3_backend {
# Local Garage instance
server 127.0.0.1:3900;
# Additional instances
server 192.168.1.3:3900;
# Domain names also work
server garage1.example.com:3900;
# Backup server only used if others fail
server garage-remote.example.com:3900 backup;
# Assign weights for servers with different capacities
server garage2.example.com:3900 weight=2;
}
server {
listen [::]:443 http2 ssl;
ssl_certificate /tmp/garage.crt;
ssl_certificate_key /tmp/garage.key;
# Multiple server names needed:
# - s3.garage.tld for path-based S3 requests
# - *.s3.garage.tld for vhost-based S3 requests
server_name s3.garage.tld *.s3.garage.tld;
location / {
proxy_pass http://s3_backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
# Disable buffering to a temporary file
proxy_max_temp_file_size 0;
}
}
Web Endpoint
The web endpoint configuration is similar but uses the web port (3902):
upstream web_backend {
server 127.0.0.1:3902;
server 192.168.1.3:3902;
server garage1.example.com:3902;
server garage2.example.com:3902 weight=2;
}
server {
listen [::]:443 http2 ssl;
ssl_certificate /tmp/garage.crt;
ssl_certificate_key /tmp/garage.key;
# Multiple server names:
# - *.web.garage.tld for generic websites
# - Custom domains reserved by users
server_name *.web.garage.tld example.com my-site.tld;
location / {
proxy_pass http://web_backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
Save these configurations to /etc/nginx/sites-available/garage.conf, then:
ln -s /etc/nginx/sites-available/garage.conf /etc/nginx/sites-enabled/
nginx -s reload
Apache httpd
Apache HTTP Server can also serve as a reverse proxy for Garage.
S3 API Configuration
Create a virtual host with SSL certificates:
<VirtualHost *:443>
ServerName garage.example.com
SSLCertificateFile /etc/letsencrypt/live/garage.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/garage.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
Header always set Strict-Transport-Security "max-age=31536000"
Header always add Content-Security-Policy upgrade-insecure-requests
ProxyPass "/" "http://localhost:3900/" nocanon
ProxyPreserveHost on
</VirtualHost>
The nocanon keyword is critical for presigned URLs to work correctly. Without it, Apache will canonicalize the URL path, breaking presigned URL signatures.
Web Endpoint Configuration
For static websites, change the port in the ProxyPass directive:
ProxyPass "/" "http://localhost:3902/" nocanon
Unix Socket Support
Apache can proxy via Unix sockets if Garage is configured to use them:
garage.toml:
[s3_api]
api_bind_addr = "/run/garage/s3_api.socket"
Apache config:
ProxyPass "/" "unix:/run/garage/s3_api.socket|http://localhost/" nocanon
Traefik v2
Traefik provides dynamic configuration and automatic ACME certificate management.
Basic Configuration
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.websecure]
address = ":443"
[certificatesResolvers.myresolver.acme]
email = "your-email@example.com"
storage = "acme.json"
[certificatesResolvers.myresolver.acme.httpChallenge]
entryPoint = "web"
Garage Services
Define Garage S3 and web services with load balancing:
[http.services]
[http.services.garage-s3-service.loadBalancer]
[[http.services.garage-s3-service.loadBalancer.servers]]
url = "http://xxx.xxx.xxx.xxx"
port = 3900
[[http.services.garage-s3-service.loadBalancer.servers]]
url = "http://yyy.yyy.yyy.yyy"
port = 3900
[http.services.garage-s3-service.loadBalancer.healthCheck]
path = "/health"
port = "3903"
[http.services.garage-web-service.loadBalancer]
[[http.services.garage-web-service.loadBalancer.servers]]
url = "http://xxx.xxx.xxx.xxx"
port = 3902
[http.services.garage-web-service.loadBalancer.healthCheck]
path = "/health"
port = "3903"
Routers
[http.routers]
[http.routers.garage-s3]
rule = "Host(`s3.example.org`)"
service = "garage-s3-service"
entryPoints = ["websecure"]
[http.routers.garage-s3.tls]
certResolver = "myresolver"
[http.routers.my_website]
rule = "Host(`yoururl.example.org`)"
service = "garage-web-service"
entryPoints = ["websecure"]
[http.routers.my_website.tls]
certResolver = "myresolver"
Compression Middleware
Add gzip compression before sending responses:
[http.routers]
[http.routers.my_website]
middlewares = ["compression"]
# ... other config
[http.middlewares]
[http.middlewares.compression.compress]
Caddy
Caddy provides automatic HTTPS with built-in ACME support.
Basic Configuration
s3.garage.tld, *.s3.garage.tld {
reverse_proxy localhost:3900 192.168.1.2:3900 example.tld:3900 {
health_uri /health
health_port 3903
}
}
*.web.garage.tld {
reverse_proxy localhost:3902 192.168.1.2:3902 example.tld:3902 {
health_uri /health
health_port 3903
}
}
admin.garage.tld {
reverse_proxy localhost:3903 {
health_uri /health
health_port 3903
}
}
Caching
Caddy supports a cache plugin for static website hosting:
# Global configuration
{
order cache before rewrite
cache
}
# Site specific
https:// {
cache
reverse_proxy ...
}
On-Demand TLS
Caddy can provision TLS certificates on-demand when clients first connect. Configure Garage’s check endpoint to authorize domains:
# Global configuration
{
on_demand_tls {
ask http://localhost:3903/check
interval 2m
burst 5
}
}
# Host configuration
*.web.garage.tld {
tls {
on_demand
}
reverse_proxy localhost:3902 192.168.1.2:3902 example.tld:3902
}
Never configure https:// with on-demand TLS without the global ask endpoint, as this would allow attackers to request certificates for arbitrary domains pointed at your server.
socat (Testing Only)
For quick TLS testing, socat can wrap a Garage endpoint:
socat \
"openssl-listen:443,\
reuseaddr,\
fork,\
verify=0,\
cert=/tmp/garage.pem" \
tcp4-connect:localhost:3900
This is only suitable for testing purposes, not production use.