From 915b8121d04a585c9ed7b9a6f4f3c3f36775b159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sat, 6 Jun 2026 23:12:03 +0200 Subject: [PATCH 1/2] feat(infra): enable IPv6 egress on Docker compose networks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The journal container could not reach v6-only fediverse instances (e.g. social.ullrich.is) because the compose project network was IPv4-only. The flagship host has working IPv6, and Docker >= 27 auto-allocates a ULA subnet with NAT66 masquerading when a network sets enable_ipv6, so the fix is a one-line opt-in per network. Applying it to an existing deployment requires recreating the network (docker compose down && up -d) — a brief full-stack restart on the flagship and the persistent staging project. PR-preview networks are created fresh per PR and need nothing. Co-Authored-By: Claude Opus 4.8 (1M context) --- infrastructure/docker-compose.staging.yml | 6 ++++++ infrastructure/docker-compose.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/infrastructure/docker-compose.staging.yml b/infrastructure/docker-compose.staging.yml index fe71e9b..ce4140c 100644 --- a/infrastructure/docker-compose.staging.yml +++ b/infrastructure/docker-compose.staging.yml @@ -109,6 +109,12 @@ services: memory: 256M networks: + # IPv6 egress for staging/preview journals, matching production — see + # the production compose file for details. Preview networks are created + # fresh per PR; the persistent staging network needs a one-time + # down/up to pick this up. + default: + enable_ipv6: true trails-shared: external: true name: trails-shared diff --git a/infrastructure/docker-compose.yml b/infrastructure/docker-compose.yml index 1390257..37c591b 100644 --- a/infrastructure/docker-compose.yml +++ b/infrastructure/docker-compose.yml @@ -250,7 +250,13 @@ services: networks: # Default project network — kept implicit for production services. + # IPv6 is enabled so the journal can federate with v6-only instances + # (e.g. social.ullrich.is). Docker ≥27 auto-allocates a ULA subnet and + # NAT66-masquerades egress via the host's public v6 address. + # NOTE: flipping this on an existing deployment requires recreating the + # network: `docker compose down && docker compose --env-file .env up -d`. default: + enable_ipv6: true # Created by this compose project with a fixed (un-namespaced) name so the # staging compose project can attach to it via `external: true`. Only # postgres is joined here on the production side; staging containers join From 2d8046b9a6ea387afd0c0ad24650724091689a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sat, 6 Jun 2026 23:21:25 +0200 Subject: [PATCH 2/2] fix(infra): IPv6 belongs on trails-shared, not a staging default network MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #466: staging and preview services attach only to the externally-named trails-shared network (created by the production compose project) — they have no per-project default network, so the enable_ipv6 added to the staging file was dead config. Move the flag to trails-shared where it takes effect. Rollout requires recreating trails-shared on the flagship: detach staging, previews, and production postgres, remove the network, re-up. Production journal/planner are unaffected (they use the default network). Co-Authored-By: Claude Opus 4.8 (1M context) --- infrastructure/docker-compose.staging.yml | 9 +++------ infrastructure/docker-compose.yml | 4 ++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/infrastructure/docker-compose.staging.yml b/infrastructure/docker-compose.staging.yml index ce4140c..0d4da27 100644 --- a/infrastructure/docker-compose.staging.yml +++ b/infrastructure/docker-compose.staging.yml @@ -109,12 +109,9 @@ services: memory: 256M networks: - # IPv6 egress for staging/preview journals, matching production — see - # the production compose file for details. Preview networks are created - # fresh per PR; the persistent staging network needs a one-time - # down/up to pick this up. - default: - enable_ipv6: true + # Staging/preview services attach only to trails-shared — created (and + # IPv6-enabled) by the production compose project. There is no per-project + # default network here, so v6 egress comes from trails-shared itself. trails-shared: external: true name: trails-shared diff --git a/infrastructure/docker-compose.yml b/infrastructure/docker-compose.yml index 37c591b..ac25be8 100644 --- a/infrastructure/docker-compose.yml +++ b/infrastructure/docker-compose.yml @@ -261,8 +261,12 @@ networks: # staging compose project can attach to it via `external: true`. Only # postgres is joined here on the production side; staging containers join # to reach postgres by hostname. + # IPv6-enabled so staging/preview journals — whose only network this is — + # get v6 federation egress like production. Recreating it on a live host + # requires detaching staging, previews, and postgres first. trails-shared: name: trails-shared + enable_ipv6: true volumes: caddy_data: