Docker dev database: Atlas dials the host's default gateway instead of loopback for 0.0.0.0-published ports
Summary
When Atlas provisions its dev database via a docker:// URL, it publishes the container port with an unqualified -p, so Docker reports HostIp: 0.0.0.0. Atlas then appears to resolve 0.0.0.0 to a routable host address and dials the machine's default gateway rather than loopback. On any host whose LAN gateway does not answer on that port, Atlas hangs until it times out:
Error: timeout: postgres: querying system variables: dial tcp 192.168.10.1:32794: connect: operation timed out192.168.10.1 is my LAN router. The dev container is listening and reachable the whole time — on 127.0.0.1.
This makes atlas migrate diff unusable with the built-in dev database on affected networks, and the error gives no hint that the address is the problem, so it reads as a broken project configuration.
Steps to reproduce
Any config using the built-in dev database is enough:
data "external_schema" "tenant" {
program = ["go", "run", "./tools/schema/tenant"]
}
env "tenant" {
src = data.external_schema.tenant.url
dev = "docker://postgres/18/dev?search_path=public"
migration {
dir = "file://internal/migrations/tenant"
format = goose
}
}$ atlas migrate validate --env tenant
Error: timeout: postgres: querying system variables: dial tcp 192.168.10.1:32799: connect: operation timed outIt reproduces equally with atlas schema inspect --url "docker://postgres/18/dev?search_path=public", so no project file is required.
Evidence that the port is fine and the address is not
Docker publishes on all interfaces:
$ docker inspect <container> --format '{{json .NetworkSettings.Ports}}'
{"5432/tcp":[{"HostIp":"0.0.0.0","HostPort":"32802"},{"HostIp":"::","HostPort":"32802"}]}192.168.10.1 is the default gateway, not a Docker address:
$ route -n get default
gateway: 192.168.10.1
interface: en6
$ ipconfig getifaddr en0
192.168.10.60The published port is open on loopback and unreachable on the address Atlas chooses:
$ nc -z -G 5 127.0.0.1 32803
Connection to 127.0.0.1 port 32803 [tcp/*] succeeded!
$ nc -z -G 5 192.168.10.1 32803
(times out)Expected behaviour
When the published HostIp is 0.0.0.0 (or ::), connect to 127.0.0.1 (or ::1). A wildcard bind means "all interfaces on this host", and loopback is the only one guaranteed to reach the container's published port. Resolving it to the default gateway dials a different machine entirely.
Versions
- Atlas
v1.3.0(stable release) — reproduces - Atlas
v1.3.1-2992e0f-canary— reproduces identically
Both were tested on the same machine against the same config, so this is not a regression in the canary build.
- macOS 15 (Darwin 25.5.0), arm64
- Docker provided by OrbStack 29.4.0 (Docker API-compatible)
- Host on a
192.168.10.0/24LAN, gateway192.168.10.1
I would expect this to affect any Docker runtime that reports 0.0.0.0 for a published port, which is the default for an unqualified -p. It is likely network-dependent rather than runtime-dependent: on a LAN whose gateway happens to accept connections on the chosen high port, or where the resolution lands elsewhere, the same setup works — which makes it look intermittent across machines and locations.
Workaround
Point ATLAS_DEV_URL (or dev) at a Postgres published explicitly on loopback:
docker run -d --rm --name atlas-dev \
-e POSTGRES_USER=atlas -e POSTGRES_PASSWORD=atlas -e POSTGRES_DB=atlas \
-p 127.0.0.1:55999:5432 postgres:18-alpine
export ATLAS_DEV_URL="postgres://atlas:[email protected]:55999/atlas?sslmode=disable&search_path=public"The docker block would presumably sidestep the URL, but it requires atlas login, so it is not available as a workaround for unauthenticated use.
Source: ariga/atlas