Every container gets a network connection, but which containers can talk to each other, and by what name, depends on the network they join. Getting this right lets your app reach its database by name, keeps internal services off the public internet and lets one reverse proxy serve several Compose projects. This guide covers the network types you will use on a VPS.
Before you start
- A Linux VPS with Docker Engine and the Compose plugin. See Install Docker and Docker Compose.
- A user that can run
dockercommands. - A basic idea of ports and IP addresses.
List the networks Docker creates on install:
docker network lsYou will see bridge, host and none.
The default bridge network
A container started with docker run and no --network option joins the default network called bridge. Docker gives it a private address, usually in 172.17.0.0/16, and uses NAT so it can reach the internet.
The default bridge has one big limitation: containers on it cannot find each other by name. They can only use IP addresses, which change when containers are recreated. Treat it as a fallback, not something to build on.
User-defined bridge networks
A network you create yourself is also a bridge, but it includes an internal DNS server. Containers on it can reach each other by container name or service name.
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=change-me postgres:16
docker run -d --name app --network appnet -e DB_HOST=db myapp:1.0The app connects to db:5432. You did not publish port 5432 on the host, so the database is not reachable from the internet. Only containers on appnet can see it.
Compose does this for you. Every project gets a network called <project>_default, and each service can reach the others by service name:
services:
app:
image: myapp:1.0
environment:
DB_HOST: db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: change-meTip: Only publish ports (ports:) for services the outside world must reach, usually just the reverse proxy. Everything else talks over the internal network.
Several networks in one project
You can split a stack so the proxy never sees the database:
services:
proxy:
image: nginx:stable
ports:
- "80:80"
networks: [frontend]
app:
image: myapp:1.0
networks: [frontend, backend]
db:
image: postgres:16
networks: [backend]
networks:
frontend:
backend:
internal: trueinternal: true also blocks the backend network from reaching the internet, which suits a database that never needs outbound access.
Connect containers across Compose projects
A common VPS layout is one reverse proxy stack plus several app stacks. They need a shared network. Create it once, outside any project:
docker network create proxyIn each Compose file that should join it, mark the network as external:
services:
app:
image: myapp:1.0
networks: [proxy, default]
networks:
proxy:
external: trueThe proxy container can now reach app by its service name. Keeping default in the list lets the app still talk to its own database. Run many Docker Compose projects on one VPS shows a full folder layout built on this pattern.
You can also attach a running container to a network without recreating it:
docker network connect proxy myapp-app-1Host networking
With --network host, the container shares the VPS's own network stack. It has no separate IP, and port mapping does not apply: if the app listens on 8080, it is on the host's port 8080 directly.
docker run -d --name exporter --network host prom/node-exporterUse host mode for tools that need to see the host's real interfaces, such as some monitoring agents, or for very high packet rates. For ordinary web apps, a bridge network is safer because it isolates ports.
The none network
--network none gives a container only a loopback interface. Use it for batch jobs that should never touch the network, such as processing files in a volume.
Inspect networks
See a network's subnet, gateway and attached containers:
docker network inspect appnetShow which networks a container is on:
docker inspect app --format '{{json .NetworkSettings.Networks}}'Remove networks no container uses:
docker network pruneCheck it works
Test name resolution from inside the app container:
docker run --rm --network appnet busybox:stable nslookup db
docker run --rm --network appnet busybox:stable ping -c 2 dbThe first command should return an address for db. The second should get replies from the database container.
Troubleshooting
could not translate host name "db": the two containers are on different networks, or one of them is on the default bridge. Put both on the same user-defined network.network proxy declared as external, but could not be found: create it first withdocker network create proxy.Pool overlaps with other one on this address space: a new network's subnet clashes with an existing one or with a VPN. Remove unused networks withdocker network prune, or setdefault-address-poolsin/etc/docker/daemon.json.- A port is reachable from the internet even though UFW denies it: Docker publishes ports through its own firewall rules. Bind to
127.0.0.1or see Fix UFW and Docker bypassing the firewall.
Next steps
- Fix outbound problems: Fix DNS and internet access inside Docker containers.
- Lay out several stacks behind one proxy: Run many Docker Compose projects on one VPS.
