Compose 514 creates one network per project, <project>_default, and attaches every service to it, so services find each other by service name through Docker 514 's DNS (User-Defined Networks): that is why PGHOST: db works. Named volumes under the top-level volumes: key become <project>_<name>. Bring the stack up, and resolve db from a one-off api container (run starts a fresh container of a service):
docker compose up -d 2>&1 | grep -E '(Network|Volume) .* Created' | sort -u
docker compose run --rm --no-deps api getent hosts dbNetwork l3-booknest_default Created Volume l3-booknest_pgdata Created Container l3-booknest-api-run-dded7da81aee Creating Container l3-booknest-api-run-dded7da81aee Created 172.19.0.2 db
The names carry the project prefix, so two projects on one host, or two copies of the same project under different names, never collide. Declare more networks to isolate tiers: with networks: [frontend] on a web service and networks: [frontend, backend] on the API, only the API could reach a database attached to backend alone. A network or volume that must outlive the project, or that another project owns, is marked external: true and must already exist. Volumes survive docker compose down unless you add -v, so the catalog stays put between sessions.