fix(galactus): give containers Tailscale's resolver so MagicDNS names resolve
With the image fixed, the API got as far as connecting and then died with
Prisma P1001 "can't reach database server". The cause is DNS, not routing.
galactus runs systemd-resolved, whose 127.0.0.53 stub is unreachable from
inside a container, so Docker falls back to the upstream resolver in
/run/systemd/resolve/resolv.conf — the LAN router, which knows nothing about
the tailnet. Verified from a probe container on galactus: resolving
galactus.tail01aa2.ts.net fails outright, while `nc 100.103.77.46 3306` is
OPEN. Only the lookup was broken.
Pin the api and web services to Tailscale's own resolver (100.100.100.100,
the same anycast address on every tailnet) with this tailnet's search suffix.
Both are overridable via TAILSCALE_DNS / TAILNET_SUFFIX. db and minio need
nothing — they make no outbound calls.
Verified end to end: the published image, unmodified, with only these DNS
settings, boots on galactus against the real database and serves
/health {"status":"ok"}
/version {"service":"api","version":"master","gitSha":"3ff56e6b..."}
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,16 @@
|
|||||||
# name galactus's own address and the published port — exactly as on cubex
|
# name galactus's own address and the published port — exactly as on cubex
|
||||||
# today. Do not "simplify" them to `mysql:3306`.
|
# today. Do not "simplify" them to `mysql:3306`.
|
||||||
#
|
#
|
||||||
|
# ...which means these containers have to resolve galactus's MagicDNS name, and
|
||||||
|
# by default they CANNOT. The host runs systemd-resolved, whose 127.0.0.53 stub
|
||||||
|
# is unreachable from a container, so Docker falls back to the upstream resolver
|
||||||
|
# in /run/systemd/resolve/resolv.conf — the LAN router, which knows nothing
|
||||||
|
# about the tailnet. Routing to 100.x works fine; only the lookup fails, and the
|
||||||
|
# API dies with Prisma P1001 "can't reach database server". Pointing the
|
||||||
|
# containers at Tailscale's own resolver fixes it. 100.100.100.100 is Tailscale's
|
||||||
|
# fixed anycast MagicDNS address (identical on every tailnet); the search domain
|
||||||
|
# is this tailnet's suffix.
|
||||||
|
#
|
||||||
# The web image is NOT URL-baked: the browser's API origin is injected at
|
# The web image is NOT URL-baked: the browser's API origin is injected at
|
||||||
# runtime from API_ORIGIN (apps/web/src/app/layout.tsx), so the same image works
|
# runtime from API_ORIGIN (apps/web/src/app/layout.tsx), so the same image works
|
||||||
# for any deployment. APP_VERSION / GIT_SHA / BUILD_DATE come baked in from
|
# for any deployment. APP_VERSION / GIT_SHA / BUILD_DATE come baked in from
|
||||||
@@ -27,6 +37,10 @@ services:
|
|||||||
# survives stack renames; the compose service name does not.
|
# survives stack renames; the compose service name does not.
|
||||||
labels:
|
labels:
|
||||||
io.jorgecuadros.role: "api"
|
io.jorgecuadros.role: "api"
|
||||||
|
dns:
|
||||||
|
- ${TAILSCALE_DNS:-100.100.100.100}
|
||||||
|
dns_search:
|
||||||
|
- ${TAILNET_SUFFIX:-tail01aa2.ts.net}
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL must be set}
|
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL must be set}
|
||||||
SESSION_SECRET: ${SESSION_SECRET:?SESSION_SECRET must be set}
|
SESSION_SECRET: ${SESSION_SECRET:?SESSION_SECRET must be set}
|
||||||
@@ -59,6 +73,12 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
labels:
|
labels:
|
||||||
io.jorgecuadros.role: "web"
|
io.jorgecuadros.role: "web"
|
||||||
|
# Next server-side rendering can call the API by API_ORIGIN, which is the
|
||||||
|
# same MagicDNS name — so the web container needs the resolver too.
|
||||||
|
dns:
|
||||||
|
- ${TAILSCALE_DNS:-100.100.100.100}
|
||||||
|
dns_search:
|
||||||
|
- ${TAILNET_SUFFIX:-tail01aa2.ts.net}
|
||||||
environment:
|
environment:
|
||||||
# Public API URL the browser calls (injected at runtime, see layout.tsx).
|
# Public API URL the browser calls (injected at runtime, see layout.tsx).
|
||||||
API_ORIGIN: ${API_ORIGIN:?API_ORIGIN must be set}
|
API_ORIGIN: ${API_ORIGIN:?API_ORIGIN must be set}
|
||||||
|
|||||||
@@ -148,6 +148,30 @@ service DNS: db, minio and app are three separate stacks, so three separate
|
|||||||
networks. `DATABASE_URL` and `S3_ENDPOINT` name the host and its published
|
networks. `DATABASE_URL` and `S3_ENDPOINT` name the host and its published
|
||||||
port. Do not "simplify" them to `mysql:3306`.
|
port. Do not "simplify" them to `mysql:3306`.
|
||||||
|
|
||||||
|
### galactus is addressed by MagicDNS, and containers need help resolving it
|
||||||
|
|
||||||
|
galactus is Tailscale-only once it is installed in the office, so every URL
|
||||||
|
names `galactus.tail01aa2.ts.net`. Its LAN IP is a DHCP lease and has already
|
||||||
|
drifted once — never put a `192.168.4.x` address in a secret.
|
||||||
|
|
||||||
|
Containers on galactus cannot resolve that name by default. The host runs
|
||||||
|
systemd-resolved, whose `127.0.0.53` stub is unreachable from inside a
|
||||||
|
container, so Docker falls back to the upstream resolver listed in
|
||||||
|
`/run/systemd/resolve/resolv.conf` — the LAN router, which knows nothing about
|
||||||
|
the tailnet. Routing to `100.x` works fine; only the *lookup* fails, and the
|
||||||
|
symptom is Prisma **P1001 "can't reach database server"** on a container that
|
||||||
|
otherwise started cleanly.
|
||||||
|
|
||||||
|
`deploy/galactus/jorgecuadros-app.compose.yml` therefore pins the resolver:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dns: [100.100.100.100] # Tailscale's fixed anycast MagicDNS address
|
||||||
|
dns_search: [tail01aa2.ts.net] # this tailnet's suffix
|
||||||
|
```
|
||||||
|
|
||||||
|
Both are overridable (`TAILSCALE_DNS`, `TAILNET_SUFFIX`) if the tailnet changes.
|
||||||
|
Browser-facing origins need none of this — those names resolve on the client.
|
||||||
|
|
||||||
## Replication
|
## Replication
|
||||||
|
|
||||||
galactus's MySQL is the **master**; every other MySQL in the estate is a
|
galactus's MySQL is the **master**; every other MySQL in the estate is a
|
||||||
|
|||||||
Reference in New Issue
Block a user