Engine creates a Linux bridge called docker0 (172.17.0.1/16 on this host) and attaches every container you start without --network to it, through a veth pair: one end becomes the container's eth0, the other plugs into the bridge. Containers on it can reach each other and, through NAT, the outside world. What they cannot do is find each other by name:
docker run -d --name l3-db --env-file booknest.env postgres:18
sleep 5
IP=$(docker inspect l3-db --format '{{.NetworkSettings.Networks.bridge.IPAddress}}')
docker run --rm alpine:3 sh -c \
"getent hosts l3-db || echo 'l3-db: not found'; nc -zv -w 2 $IP 5432"
docker run --rm alpine:3 grep nameserver /etc/resolv.conf2307367c0359db18e9fc549c5b5586e9060ef8128c0a7d7fcafd987b8824346e l3-db: not found 172.17.0.3 (172.17.0.3:5432) open nameserver 10.255.255.254
The database is reachable at 172.17.0.3, but that address is handed out in start order and changes when containers restart, so no configuration file should contain it. Containers on the default bridge simply get a copy of the host's DNS settings (here WSL 6 's resolver, 10.255.255.254), which knows nothing about containers. The old workaround, --link, wrote names into /etc/hosts and is a deprecated legacy feature.
The default bridge is also shared by every container started without a network, so unrelated projects can reach each other's databases. Use it only for quick experiments.