How to Build a Containerized Homelab with Docker
Docker is the soul of any modern homelab. Learn how to use Docker Compose to build a fully featured homelab running Pi-hole, Home Assistant, Nextcloud, WireGuard, and other popular self-hosted services.
1. Why Containerize Your Homelab?
Containerization with <strong class="text-white">Docker</strong> is the most efficient way to run multiple services on a homelab. Instead of installing each service directly on your OS (which leads to dependency conflicts, version hell, and messy cleanup), each service runs in its own isolated container with exactly the dependencies it needs.
Docker Compose lets you define your entire Homelab as a <code class="text-green-400">docker-compose.yml</code> file. One command brings up your entire Homelab; one command shuts it down cleanly. Moving to a new server? Copy the compose file, run <code class="text-green-400">docker compose up -d</code>, and everything is running in minutes.
🔄 Isolation
Each service is isolated. Upgrading Home Assistant won't break your Pi-hole.
📦 Clean removal
docker compose down removes everything. No leftover config files, no uninstall scripts.
💾 Version control
Your entire Homelab config lives in one YAML file. Put it in Git and track changes.
🔄 Reproducible
Same config on your dev machine, test server, and production Homelab.
2. Docker Compose Setup
Assuming you already have Docker installed (from the Build From Scratch guide), set up a clean directory structure:
Create a homelab directory
Organize your Homelab config in one place.
Create service directories
Each service gets its own subdirectory for config files.
Write docker-compose.yml
Define all your services in one YAML file.
mkdir -p ~/homelab/{pi-hole,nginx-proxy-manager,wireguard,home-assistant,portainer}
cd ~/homelab3. Docker Networking
Docker creates isolated networks for your containers. By default, containers on the same Docker network can talk to each other by container name. Containers not on the same network are isolated.
For a Homelab, use a <strong class="text-white">Docker network bridge</strong> to let your services communicate with each other, and expose only specific ports to the host.
Create a Docker network
Create a shared network for your Homelab services.
Attach containers to the network
Each service in docker-compose.yml connects to this network.
# Create a Docker network for homelab services
docker network create homelab-net
# Or in docker-compose.yml (auto-creates the network)
services:
pihole:
networks:
- homelab-net
networks:
homelab-net:
name: homelab-net4. Essential Homelab Services to Containerize
Here are the most popular containerized Homelab services, with one-line Docker Compose examples:
🛡️ Pi-hole — Network-wide Ad Blocking
Block ads and trackers at the DNS level for every device on your network.
🏠 Home Assistant — Home Automation
Control smart home devices, automate routines, and monitor sensors — all locally.
🔐 WireGuard — VPN Server
Access your entire Homelab network from anywhere. More secure than port forwarding.
📂 Nextcloud — Personal Cloud
Google Drive / Dropbox alternative. File sync, photo backup, notes, and calendar.
🌐 Nginx Proxy Manager — Reverse Proxy
Manage HTTPS certificates and route external traffic to internal services.
📊 Uptime Kuma — Monitoring
Monitor service uptime with beautiful dashboards and instant alerts.
🖥️ Portainer — Docker Management UI
Web UI to manage containers, images, networks, and volumes without CLI.
5. Complete docker-compose.yml Example
A production-ready Docker Compose setup for a Homelab with the most essential services. Save this as ~/homelab/docker-compose.yml:
version: '3.8'
services:
# ── DNS & Ad Blocking ──────────────────────────────────
pihole:
image: pihole/pihole:latest
container_name: pihole
environment:
TZ: 'Asia/Shanghai'
WEBPASSWORD: 'YourSecurePassword123'
DNSMASQ_LISTENING: 'all'
volumes:
- ./pi-hole/etc-pihole:/etc/pihole
- ./pi-hole/etc-dnsmasq:/etc/dnsmasq.d
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp" # Admin UI (restrict access via reverse proxy)
networks:
- homelab
restart: unless-stopped
dns:
- 127.0.0.1
- 1.1.1.1
# ── Reverse Proxy ──────────────────────────────────────
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: npm
ports:
- "80:80"
- "443:443"
- "81:81" # Admin UI
environment:
DB_MYSQL_HOST: "npm_db"
DB_MYSQL_PORT: 3306
DB_MYSQL_USER: "npm"
DB_MYSQL_PASSWORD: "npm_password"
DB_MYSQL_NAME: "npm"
volumes:
- ./nginx-proxy-manager/data:/data
- ./nginx-proxy-manager/letsencrypt:/etc/letsencrypt
networks:
- homelab
restart: unless-stopped
npm_db:
image: jc21/mariadb:latest
container_name: npm_db
environment:
MYSQL_ROOT_PASSWORD: "root_password"
MYSQL_DATABASE: "npm"
MYSQL_USER: "npm"
MYSQL_PASSWORD: "npm_password"
volumes:
- ./nginx-proxy-manager/db:/var/lib/mysql
networks:
- homelab
restart: unless-stopped
depends_on:
- nginx-proxy-manager
# ── VPN ────────────────────────────────────────────────
wireguard:
image: linuxserver/wireguard
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
PUID: "1000"
PGID: "1000"
TZ: "Asia/Shanghai"
SERVERURL: "your-domain-or-ip"
SERVERPORT: "51820"
PEERS: "3"
PEERDNS: "10.8.0.1"
INTERNAL_SUBNET: "10.8.0.0"
volumes:
- ./wireguard/config:/config
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
networks:
- homelab
restart: unless-stopped
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
# ── Monitoring ─────────────────────────────────────────
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
ports:
- "3001:3001"
volumes:
- ./uptime-kuma:/app/data
networks:
- homelab
restart: unless-stopped
# ── Docker Management ──────────────────────────────────
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
ports:
- "9000:9000"
- "9443:9443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer/data:/data
networks:
- homelab
restart: unless-stopped
networks:
homelab:
name: homelab
driver: bridge6. Backup & Maintenance
📁 Backup volumes regularly
Docker volumes persist data. Back them up to your NAS or an external drive with a cron job. Services like Portainer have built-in volume backup.
🔄 Watchtower for auto-updates
Add Watchtower to automatically pull the latest container images and restart services. Or update manually: docker compose pull && docker compose up -d.
📊 Monitor disk space
Docker images, volumes, and logs consume disk space. Run docker system df periodically and docker system prune to clean up.
📝 Keep your compose file in Git
Put your docker-compose.yml in a Git repo. Every change is tracked. On a new server, clone the repo and run docker compose up -d.
Frequently Asked Questions
Should I use Docker or Podman in my Homelab?
Docker is the standard for Homelabs — more documentation, more community support, and more one-click compose files available. Podman is daemonless and considered more secure, but Docker is simpler for beginners and works identically for Homelab use cases.
What is the difference between Docker and LXC containers?
Docker containers share the host kernel and are optimized for running applications (web servers, databases). LXC containers are more like lightweight VMs — they can run a full Linux OS with their own init system. Proxmox LXC containers are better for running multiple isolated Linux systems; Docker is better for running multiple applications on one Linux host.
How do I access my Homelab services from my phone/laptop?
Set your router's DHCP to give your Docker host a static IP (e.g., 192.168.1.100). Access services at http://192.168.1.100:port. For external access, use WireGuard VPN or Nginx Proxy Manager with a domain name and HTTPS.
Can Docker containers communicate with each other by name?
Yes — containers on the same Docker network can reach each other by service name. If you have a service named "pihole", another container can reach it at http://pihole:80. This is Docker's internal DNS.
How do I update a Docker container?
Pull the new image and recreate the container: docker compose pull && docker compose up -d. Docker Compose will automatically pull the latest image and recreate the container while preserving volumes (your data).