Docker Networking Explained for Home Servers
Understand bridge, host and macvlan networks, container DNS, and how to expose services safely behind a reverse proxy.
Why Docker networking confuses everyone
Docker networking is the single biggest source of "why can't my containers talk to each other?" questions. Once you understand a few concepts, it becomes predictable.
The default bridge vs. user-defined bridges
When you install Docker you get a default bridge network, but you should almost always create your own user-defined bridge (Compose does this automatically per project). The key difference: on a user-defined bridge, containers can reach each other by name.
services:
app:
image: my/app
depends_on: [db]
db:
image: postgres:16Here app can connect to the database at the hostname db — Docker provides internal DNS. No IP addresses needed.
Publishing ports
Containers are isolated by default. To reach a service from your LAN, publish a port:
ports:
- "8080:80" # host:containerThis maps port 8080 on the host to port 80 in the container. Only publish the ports you actually need — everything else stays private on the Docker network.
Host networking
network_mode: host removes network isolation and lets the container share the host's network stack. It's occasionally required (some media and discovery tools need it) but skips Docker's port mapping and reduces isolation, so use it sparingly.
macvlan: when a container needs a real IP
Sometimes a container must appear as a genuine device on your LAN — the classic case is Pi-hole listening on port 53. A macvlan network gives the container its own MAC address and IP. The main gotcha: the Docker host itself can't talk to the container over macvlan without an extra shim interface.
The clean pattern: one reverse proxy
Instead of publishing a different port for every app, put a reverse proxy in front:
- Create a shared Docker network (e.g.
proxy). - Attach the reverse proxy and each web app to it.
- The proxy reaches apps by container name; you expose only ports 80/443 to your LAN.
- Add automatic HTTPS with Caddy or Nginx Proxy Manager.
This gives you clean hostnames like photos.home.example.com, one place for TLS, and far fewer exposed ports.
Quick troubleshooting checklist
- Containers can't resolve each other? Make sure they're on the same user-defined network.
- Port already in use? Something else on the host is bound to it — change the host side of the mapping.
- Proxy can't reach an app? Confirm both are attached to the shared proxy network and you're using the container name, not
localhost.
Related articles
Docker Compose Best Practices for Home Servers
Structure stacks, volumes, networks, secrets and updates so your Compose homelab stays clean, secure and easy to rebuild.
How to Install Portainer
Get a web UI for managing Docker containers, images, volumes and stacks in minutes — with security hardening and multi-environment tips.
How to Install Docker on Ubuntu
A step-by-step tutorial to install Docker Engine and Docker Compose on Ubuntu Server, then run your first container.