Table of Contents
Running everything in Docker containers lets you spin services up and down in seconds, keep configs in portable compose files, and isolate each service cleanly. This guide covers the core stack we use in the homelab — from DNS and proxy routing all the way through media management and remote access.
The architecture assumes a single Docker host (Unraid, Debian, or Ubuntu) with an NGINX Proxy Manager container handling all reverse proxy duties and a single wildcard SSL certificate covering every subdomain.
-
1
Install Docker Engine
# Quick install (Debian/Ubuntu) curl -fsSL https://get.docker.com | sh usermod -aG docker $USER # add your user to docker group newgrp docker -
2
Install Docker Compose Plugin
apt install docker-compose-plugin docker compose version # confirm: Docker Compose version v2.x -
3
Create appdata Directory
Standardize your config storage location — everything goes under
/opt/appdata/:mkdir -p /opt/appdata/{portainer,npm,adguard,plex,overseerr,ddns}
Portainer provides a GUI for Docker — manage stacks, containers, images, and volumes without the CLI. Deploy it first so you can manage everything else through the UI.
Access: https://<host-ip>:9443 — create admin account on first visit.
/var/run/docker.sock to control Docker. On Unraid, this is already available — add Portainer via Community Apps instead of running this command manually.NPM routes external traffic to internal services by hostname. Pair with a Cloudflare wildcard SSL cert and you get HTTPS on every subdomain automatically.
Admin panel: http://<host-ip>:81 — default login: admin@example.com / changeme
See the full NPM walkthrough for SSL and proxy host setup.
AdGuard Home acts as a local DNS server that blocks ads and tracking at the network level. All devices on the LAN point their DNS at this container.
systemctl stop systemd-resolved && systemctl disable systemd-resolved, then edit /etc/resolv.conf to point at your router.After setup, configure your router's DHCP server to hand out the AdGuard host IP as the DNS server for all LAN clients.
If your ISP assigns a dynamic public IP (most do), use a DDNS container to automatically update your Cloudflare A record whenever the IP changes.
Use a scoped Cloudflare API token with Zone:DNS:Edit permissions — not the Global API Key.
Overseerr connects to your Plex library and lets users request movies/shows — it integrates with Radarr and Sonarr to handle the download.
Full list of battle-tested containers from the homelab stack:
| Service | Purpose | Image |
|---|---|---|
| Portainer CE | Docker GUI | portainer/portainer-ce |
| Nginx Proxy Manager | Reverse proxy + SSL | jc21/nginx-proxy-manager |
| AdGuard Home | DNS ad blocking | adguard/adguardhome |
| Plex | Media streaming | linuxserver/plex |
| Overseerr | Media request portal | sctx/overseerr |
| Radarr | Movie management | linuxserver/radarr |
| Sonarr | TV show management | linuxserver/sonarr |
| Cloudflare DDNS | Dynamic DNS updater | oznu/cloudflare-ddns |
| Watchtower | Auto-update containers | containrrr/watchtower |