Route s3.asxp.io to Garage
All checks were successful
Deploy storage stack / deploy (push) Successful in 20s

The data is copied and verified and both apps sign with their Garage
keys, so send s3.asxp.io to Garage's S3 API. MinIO loses its route and
stays up, unrouted, as the way back.

Garage answers no anonymous S3 request. Unsigned GET and HEAD of
/public/<key> go to its web endpoint instead, with the Host header the
endpoint picks the bucket by, so the links handed out so far keep
working. Signed requests for that bucket still reach the S3 API.

Assisted-by: Claude:fable-5-2
This commit is contained in:
Sergei Poljanski 2026-11-11 03:44:37 +04:00
commit bdfc028ca1
Signed by: asxpi
GPG key ID: 4F8851660FA4121B

View file

@ -36,6 +36,8 @@ services:
- storage - storage
restart: "no" restart: "no"
# Kept only as the way back until Garage has proven itself: not routed,
# not bootstrapped, its data frozen as of the switch.
minio: minio:
image: minio/minio:latest image: minio/minio:latest
container_name: storage-minio container_name: storage-minio
@ -49,25 +51,14 @@ services:
- minio_data:/data - minio_data:/data
networks: networks:
- storage - storage
- traefik
healthcheck: healthcheck:
test: ["CMD", "mc", "ready", "local"] test: ["CMD", "mc", "ready", "local"]
interval: 5s interval: 5s
timeout: 5s timeout: 5s
retries: 10 retries: 10
labels: labels:
- "traefik.enable=true" - "traefik.enable=false"
- "traefik.docker.network=traefik"
- "traefik.http.routers.minio-s3.rule=Host(`s3.asxp.io`)"
- "traefik.http.routers.minio-s3.entrypoints=websecure"
- "traefik.http.routers.minio-s3.tls=true"
- "traefik.http.routers.minio-s3.tls.certresolver=letsencrypt"
- "traefik.http.routers.minio-s3.service=minio-s3"
- "traefik.http.services.minio-s3.loadbalancer.server.port=9000"
# Replaces MinIO (abandoned upstream; its images are gone from the
# registries). Not routed yet: MinIO still answers s3.asxp.io until the data
# is copied and the apps are switched.
garage: garage:
image: dxflrs/garage:v2.5.0 image: dxflrs/garage:v2.5.0
container_name: storage-garage container_name: storage-garage
@ -87,7 +78,28 @@ services:
timeout: 5s timeout: 5s
retries: 10 retries: 10
labels: labels:
- "traefik.enable=false" - "traefik.enable=true"
- "traefik.docker.network=traefik"
# The S3 API.
- "traefik.http.routers.garage-s3.rule=Host(`s3.asxp.io`)"
- "traefik.http.routers.garage-s3.entrypoints=websecure"
- "traefik.http.routers.garage-s3.tls=true"
- "traefik.http.routers.garage-s3.tls.certresolver=letsencrypt"
- "traefik.http.routers.garage-s3.service=garage-s3"
- "traefik.http.services.garage-s3.loadbalancer.server.port=3900"
# Anonymous reads of the public bucket: unsigned GET/HEAD of
# /public/<key> goes to the web endpoint as Host public.web.garage,
# path /<key>. Signed requests still reach the S3 API above.
- "traefik.http.routers.garage-public.rule=Host(`s3.asxp.io`) && PathPrefix(`/public/`) && (Method(`GET`) || Method(`HEAD`)) && !HeaderRegexp(`Authorization`, `.+`) && !QueryRegexp(`X-Amz-Signature`, `.+`)"
- "traefik.http.routers.garage-public.priority=1000"
- "traefik.http.routers.garage-public.entrypoints=websecure"
- "traefik.http.routers.garage-public.tls=true"
- "traefik.http.routers.garage-public.tls.certresolver=letsencrypt"
- "traefik.http.routers.garage-public.service=garage-web"
- "traefik.http.routers.garage-public.middlewares=garage-public-strip,garage-public-host"
- "traefik.http.middlewares.garage-public-strip.stripprefix.prefixes=/public"
- "traefik.http.middlewares.garage-public-host.headers.customrequestheaders.Host=public.web.garage"
- "traefik.http.services.garage-web.loadbalancer.server.port=3902"
volumes: volumes:
pg_data: pg_data: