Reading time: ~15 minutes
Audience: Homelab beginners who have installed Linux or Proxmox
Prerequisites: A Linux VM or LXC with root/sudo access
What Is Docker Compose?
Overview
Docker Compose is a tool that lets you define and run multi-container applications using a single YAML file (docker-compose.yml). Instead of running long docker run commands with dozens of flags, you declare your entire stack — services, networks, volumes, and environment variables — in one readable file.
For homelab users, Compose is the standard way to deploy self-hosted apps like Nextcloud, Immich, Jellyfin, Pi-hole, and Portainer. A typical homelab runs 10–30 containers orchestrated through Compose files.
Key Benefits
| Benefit | Explanation |
|---|---|
| Declarative | Define once, deploy repeatedly across machines |
| Version controlled | Your docker-compose.yml is infrastructure-as-code |
| Isolated networks | Services talk to each other without exposing ports to the host |
| Persistent volumes | Data survives container deletion and updates |
| One-command updates | docker compose pull && docker compose up -d |
Prerequisites
Hardware Requirements
| Setup | RAM | Storage | Notes |
|---|---|---|---|
| Minimal (5–10 containers) | 2 GB | 20 GB SSD | Intel N100, Raspberry Pi 5, or old laptop |
| Comfortable (15–30 containers) | 4–8 GB | 64 GB SSD | Recommended for most homelabs |
| Heavy (30+ with databases) | 16 GB+ | 256 GB NVMe | Include swap or ZFS compression |
Software Requirements
- Docker Engine 24.0+ and Docker Compose plugin v2.20+
- A Linux distribution: Ubuntu 22.04/24.04 LTS, Debian 12, or Proxmox LXC (privileged or with nesting enabled)
Knowledge Prerequisites
- Basic Linux command line (SSH,
cd,ls,nanoorvim) - Understanding of YAML indentation (spaces, not tabs)
- A static IP or local DNS name for your server
Step 1: Install Docker and Docker Compose
Objective
Install the official Docker Engine and Compose plugin.
Step-by-Step Instructions
- Update your system:
sudo apt update && sudo apt upgrade -y
- Install required dependencies:
sudo apt install ca-certificates curl gnupg lsb-release -y
- Add Docker’s official GPG key and repository:
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
- Install Docker Engine and Compose:
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
- Add your user to the
dockergroup (log out and back in after):
sudo usermod -aG docker $USER
newgrp docker
- Verify:
docker --version
docker compose version
Expected output:
Docker version 26.x.x
Docker Compose version v2.29.x
Step 2: Write Your First docker-compose.yml
Objective
Create a Compose file that runs NGINX and a simple whoami service.
Step-by-Step Instructions
- Create a project directory:
mkdir -p ~/compose-demo && cd ~/compose-demo
- Create
docker-compose.yml:
version: "3.8"
services:
nginx:
image: nginx:alpine
container_name: nginx-demo
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
whoami:
image: traefik/whoami
container_name: whoami-demo
restart: unless-stopped
- Create a simple HTML file:
mkdir html
echo "<h1>Hello from Docker Compose!</h1>" > html/index.html
- Start the stack:
docker compose up -d
- Test:
curl http://localhost:8080
You should see “Hello from Docker Compose!”.
- View running containers:
docker compose ps
docker compose logs -f
Step 3: Deploy a Real Homelab Stack (NGINX Proxy Manager + Uptime Kuma)
Objective
Deploy a practical two-app stack with persistent volumes and a custom bridge network.
Step-by-Step Instructions
- Create a new directory:
mkdir -p ~/homelab-stack && cd ~/homelab-stack
- Create
docker-compose.yml:
version: "3.8"
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
ports:
- "80:80"
- "81:81"
- "443:443"
volumes:
- npm-data:/data
- npm-letsencrypt:/etc/letsencrypt
restart: unless-stopped
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
volumes:
- uptime-data:/app/data
restart: unless-stopped
# Access via NPM reverse proxy on port 3001
volumes:
npm-data:
npm-letsencrypt:
uptime-data:
- Launch:
docker compose up -d
- Access NGINX Proxy Manager at
http://your-server-ip:81. Default login: - Email:
[email protected] -
Password:
changeme -
Access Uptime Kuma at
http://your-server-ip:3001. Set it up to monitor your homelab services.
Pro Tip: Use NGINX Proxy Manager to give each app a local subdomain (e.g.,
npm.lan,kuma.lan) instead of remembering port numbers.
Step 4: Understanding Networks and Persistent Storage
Objective
Learn how Compose handles inter-container communication and data persistence.
Step-by-Step Instructions
Default Bridge Network: By default, Compose creates a private network. Services can reach each other by their service name as a hostname:
services:
db:
image: mariadb:11
environment:
MYSQL_ROOT_PASSWORD: secret
app:
image: nextcloud
environment:
MYSQL_HOST: db # Resolves to the MariaDB container
Named Volumes vs Bind Mounts:
| Type | Syntax | Best For |
|---|---|---|
| Named volume | volumes: app-data:/var/lib/mysql |
Databases, app state |
| Bind mount | ./config:/config |
Config files you edit by hand |
| tmpfs | type: tmpfs, target: /cache |
Ephemeral cache |
Example with both:
services:
immich:
image: ghcr.io/immich-app/immich-server:release
volumes:
- immich-upload:/usr/src/app/upload # Named volume
- /etc/localtime:/etc/localtime:ro # Bind mount
Step 5: Updating and Maintaining Containers
Objective
Safely update images without losing data.
Step-by-Step Instructions
- Navigate to your stack directory:
cd ~/homelab-stack
- Pull new images:
docker compose pull
- Recreate containers with new images:
docker compose up -d
Compose only restarts containers whose images changed. Named volumes are preserved automatically.
- Clean up old images to save disk space:
docker image prune -f
- For a full system cleanup (unused volumes, networks, images):
docker system prune -a --volumes
Warning: This deletes unused named volumes. Ensure your data is backed up.
Pro Tips
Tip 1: Use .env Files for Secrets
Never commit passwords to Git. Create .env:
DB_PASSWORD=supersecret
DOMAIN=homelab.local
Reference it in docker-compose.yml:
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
Tip 2: Enable Docker Auto-Start on Boot
sudo systemctl enable docker
Your containers will start automatically after a power outage if they use restart: unless-stopped.
Tip 3: Monitor Disk Usage
Docker can consume GBs of log files. Limit log size per container:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Tip 4: Use docker compose -f for Multiple Files
Split your homelab into logical files:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Troubleshooting Common Issues
“Permission Denied” on Bind Mounts
Docker runs as root by default. Either: - Set folder ownership to your user UID/GID - Or use named volumes instead of bind mounts
Ports Already in Use
sudo lsof -i :80
Find the conflicting service and change the host port mapping (left side):
ports:
- "8080:80" # Host 8080 → Container 80
Container Exits Immediately
Check logs:
docker compose logs <service-name>
Common causes: missing environment variables, wrong architecture (ARM64 vs AMD64), or port conflicts.
Conclusion
Summary
Docker Compose turns complex multi-app deployments into readable, version-controlled YAML files. In this guide, you installed Docker, wrote your first Compose file, deployed a real reverse-proxy stack, and learned how networks and volumes work.
Next Steps
- Deploy Nextcloud with Docker Compose
- Set up Portainer for visual container management
- Build a self-hosted media server with Jellyfin
- Compare Docker Compose vs Podman
Affiliate Opportunities
- Mini PCs: Intel N100 or N305 systems for low-power Docker hosts
- SSD Storage: Reliable SATA/NVMe drives for container volumes
- UPS: APC Back-UPS for keeping your Docker host alive during outages
Internal Linking Strategy
prerequisites→/proxmox-beginner-guidefor readers needing a VM hostreal-stack→/portainer-setup-guidefor GUI management of Compose stacksconclusion→/nextcloud-docker-composeas the next practical project
CTA
What are you self-hosting with Docker Compose? Share your favorite stack in the comments!
Subscribe to the WordForge newsletter for weekly Docker and homelab tutorials.