How to Build a Homelab in 2026: Software Stack
How to Build a Complete Homelab in 2026: Software Stack Guide
The homelab has changed. In 2020, a homelab was a repurposed desktop running Plex. In 2026, the same hardware runs 20+ services — a private cloud replacing subscriptions to Dropbox, Netflix, Zapier, LastPass, and a dozen SaaS tools — all orchestrated through Docker Compose, monitored with Uptime Kuma, and exposed securely via Cloudflare Tunnel.
This guide covers the full 2026 homelab software stack: what to run, how to run it, and how it all fits together.
The 2026 Homelab Philosophy
Before the software: one principle. Replace subscriptions, not just add complexity. Every service in your homelab should replace something you're paying for (or would pay for). A homelab that costs 20 hours to maintain but saves $0 is a hobby. A homelab that saves $2,000/year in subscriptions while taking 2 hours/month to maintain is an investment.
The stack below is built around that principle. Every component has a clear "what this replaces" answer.
Layer 1: The Foundation — OS and Virtualization
Proxmox VE (Recommended for Multi-Service Homelabs)
Proxmox VE (Type-1 hypervisor, free, Debian-based) is the recommended foundation for any homelab with more than 3–4 services. It runs on bare metal and lets you create:
- LXC containers — lightweight Linux containers with near-native performance; ideal for services that don't need full isolation
- KVM VMs — full virtual machines for Windows, pfSense, TrueNAS, or anything requiring its own kernel
- Mix and match — a TrueNAS VM for storage, LXC containers for services, isolated VMs for testing
# Install Proxmox VE on a bare metal machine
# Download ISO from proxmox.com/downloads
# Boot from USB, follow installer
# After install — disable enterprise repo (requires subscription):
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > \
/etc/apt/sources.list.d/pve-no-subscription.list
apt update && apt full-upgrade
Proxmox hardware minimums: 4 GB RAM (8+ GB recommended), 64-bit x86 CPU with virtualization extensions (Intel VT-x or AMD-V), SSD for the OS.
Ubuntu Server / Debian (Simpler Alternative)
If you're running a single machine and don't need VMs, Ubuntu Server 24.04 LTS or Debian 12 with Docker is faster to set up. Skip Proxmox unless you need VM isolation.
# Install Docker on Ubuntu Server 24.04
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Layer 2: Container Management
Portainer CE
Portainer gives you a web GUI for managing Docker containers across hosts — view logs, restart containers, browse volumes, and deploy new stacks without SSH.
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
ports:
- "9443:9443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
volumes:
portainer_data:
Access at https://your-server:9443. For multi-host setups, deploy the Portainer Agent on each host and connect them all to a single Portainer CE instance.
Dockge (Compose-Focused Alternative)
Dockge is a newer, lightweight Compose-focused UI — better if you primarily work with Docker Compose stacks rather than individual containers.
services:
dockge:
image: louislam/dockge:latest
restart: unless-stopped
ports:
- "5001:5001"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- dockge_data:/app/data
- /opt/stacks:/opt/stacks # directory where your compose files live
environment:
- DOCKGE_STACKS_DIR=/opt/stacks
Layer 3: Reverse Proxy and SSL
A reverse proxy lets you access all services via clean domain names (jellyfin.home, nextcloud.yourdomain.com) and handles SSL termination.
Nginx Proxy Manager
The most popular homelab reverse proxy. Point DNS at your server, add a proxy host in the UI, and NPM fetches Let's Encrypt certificates automatically.
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81" # Admin UI
volumes:
- npm_data:/data
- npm_letsencrypt:/etc/letsencrypt
volumes:
npm_data:
npm_letsencrypt:
Admin UI at http://your-server:81. Default credentials: admin@example.com / changeme — change on first login.
Cloudflare Tunnel (No Port Forwarding Required)
If you don't want to open ports on your router, Cloudflare Tunnel creates an outbound-only encrypted tunnel from your server to Cloudflare's edge:
# Install cloudflared
docker run -d \
--name cloudflared \
--restart unless-stopped \
cloudflare/cloudflared:latest \
tunnel --no-autoupdate run \
--token YOUR_TUNNEL_TOKEN
Configure in Cloudflare Zero Trust dashboard. Point tunnel routes to http://nginx-proxy-manager:80 or directly to services. No public IP, no port forwarding, no DynDNS required.
Layer 4: DNS
Pi-hole + Unbound
Pi-hole blocks ads and tracking at the DNS level for your entire network. Unbound adds a recursive DNS resolver, eliminating dependence on upstream DNS providers.
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80"
environment:
- TZ=America/New_York
- WEBPASSWORD=${PIHOLE_PASSWORD}
- PIHOLE_DNS_=172.20.0.3#5335 # Unbound container
volumes:
- pihole_config:/etc/pihole
- dnsmasq_config:/etc/dnsmasq.d
networks:
dns_net:
ipv4_address: 172.20.0.2
unbound:
image: mvance/unbound:latest
container_name: unbound
restart: unless-stopped
volumes:
- unbound_config:/opt/unbound/etc/unbound
networks:
dns_net:
ipv4_address: 172.20.0.3
networks:
dns_net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
volumes:
pihole_config:
dnsmasq_config:
unbound_config:
Set your router's DHCP DNS to your Pi-hole server's IP. All devices get ad-blocking automatically without configuring each device individually.
Pi-hole also handles local DNS — add custom DNS records for internal services:
jellyfin.home→192.168.1.100nextcloud.home→192.168.1.100portainer.home→192.168.1.100
Layer 5: File Storage
Nextcloud
Self-hosted Google Drive replacement. Handles files, contacts, calendars, document editing (via Nextcloud Office/Collabora), and video calls (Nextcloud Talk).
# All-in-One method (recommended)
docker run \
--name nextcloud-aio-mastercontainer \
--restart always \
--publish 80:80 \
--publish 8080:8080 \
--publish 443:443 \
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
nextcloud/all-in-one:latest
Open https://your-server-ip:8080 → enter your domain → AIO handles Let's Encrypt and starts all containers.
Replaces: Google Drive, Google Docs, Google Calendar, Google Contacts ($7–14/user/month on Google Workspace)
Immich (Photo Backup)
Immich is the best self-hosted Google Photos replacement. Face recognition, mobile backup, album sharing, search by location or date.
services:
immich-server:
image: ghcr.io/immich-app/immich-server:release
restart: unless-stopped
ports:
- "2283:2283"
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
environment:
- DB_HOSTNAME=database
- DB_USERNAME=postgres
- DB_PASSWORD=${DB_PASSWORD}
- DB_DATABASE_NAME=immich
- REDIS_HOSTNAME=redis
depends_on:
- redis
- database
immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:release
restart: unless-stopped
volumes:
- model_cache:/cache
redis:
image: redis:7-alpine
restart: unless-stopped
database:
image: tensorchord/pgvecto-rs:pg16-v0.2.0
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: postgres
POSTGRES_DB: immich
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
model_cache:
postgres_data:
Replaces: Google Photos ($3–10/month for storage), iCloud Photos ($2.99–9.99/month)
Layer 6: Media Server
Jellyfin
Free, open-source, GPL-2.0. Hardware transcoding at no cost. Zero telemetry.
services:
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- jellyfin_config:/config
- /mnt/media/movies:/data/movies
- /mnt/media/tv:/data/tv
- /mnt/media/music:/data/music
ports:
- "8096:8096"
devices:
- /dev/dri:/dev/dri # Intel/AMD hardware transcoding
volumes:
jellyfin_config:
After start: Dashboard → Playback → Transcoding → enable VA-API (Intel/AMD) or NVENC (NVIDIA).
Replaces: Plex Pass ($69.99/year), Netflix, streaming subscriptions
*arr Stack (Automated Media Management)
The *arr stack automates downloading and organizing your media library:
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
ports:
- "8989:8989"
volumes:
- sonarr_config:/config
- /mnt/media/tv:/tv
- /mnt/downloads:/downloads
radarr:
image: lscr.io/linuxserver/radarr:latest
ports:
- "7878:7878"
volumes:
- radarr_config:/config
- /mnt/media/movies:/movies
- /mnt/downloads:/downloads
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
ports:
- "9696:9696"
volumes:
- prowlarr_config:/config
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
ports:
- "8080:8080"
volumes:
- qbittorrent_config:/config
- /mnt/downloads:/downloads
- Sonarr: TV show monitoring and download automation
- Radarr: Movie monitoring and download automation
- Prowlarr: Indexer manager (connects Sonarr/Radarr to torrent/Usenet indexes)
- qBittorrent: Download client
Layer 7: Monitoring
Uptime Kuma
Single-container uptime monitoring for all your services. 90+ notification channels, public status pages, SSL/domain expiry alerts.
services:
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
restart: unless-stopped
ports:
- "3001:3001"
volumes:
- uptime_kuma_data:/app/data
- /var/run/docker.sock:/var/run/docker.sock # Docker container monitoring
volumes:
uptime_kuma_data:
First monitors to add: Nextcloud (HTTPS), Jellyfin (TCP 8096), Pi-hole (HTTP), Nginx Proxy Manager (HTTPS), Immich (HTTP 2283), Portainer (HTTPS 9443), your main domain SSL certificate expiry.
Replaces: Better Stack ($20/month), Freshping, StatusCake paid plans
Grafana + Prometheus (Advanced Metrics)
For server metrics (CPU, RAM, disk, network): add Prometheus and node_exporter alongside Uptime Kuma.
services:
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
node_exporter:
image: prom/node-exporter:latest
restart: unless-stopped
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
grafana:
image: grafana/grafana:latest
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
Import Grafana dashboard ID 1860 (Node Exporter Full) for a complete server metrics dashboard out of the box.
Layer 8: Security
Vaultwarden
Bitwarden-compatible password manager server. All vault data stays on your server.
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
ports:
- "8083:80"
volumes:
- vaultwarden_data:/data
environment:
- ADMIN_TOKEN=${VAULTWARDEN_ADMIN_TOKEN}
- SIGNUPS_ALLOWED=false # Disable after creating your account
volumes:
vaultwarden_data:
Use Bitwarden's official apps (browser extension, mobile, desktop) and point them to your Vaultwarden URL. 100% compatible.
Replaces: Bitwarden Premium ($10/year), 1Password ($36/year), LastPass ($36/year)
Authentik (Single Sign-On)
Authentik provides SSO for all your homelab services — one login across Nextcloud, Grafana, Portainer, and any other service that supports OIDC or SAML.
services:
postgresql:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${PG_PASS}
POSTGRES_USER: authentik
POSTGRES_DB: authentik
volumes:
- authentik_db:/var/lib/postgresql/data
redis:
image: docker.io/library/redis:alpine
restart: unless-stopped
server:
image: ghcr.io/goauthentik/server:2025.10
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
ports:
- "9000:9000"
- "9443:9443"
worker:
image: ghcr.io/goauthentik/server:2025.10
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
Replaces: Okta ($2/user/month), Auth0 (paid tiers), Duo ($3/user/month)
Layer 9: Network Security
Tailscale (Zero-Config VPN)
Tailscale creates a secure mesh VPN between all your devices. Access homelab services from anywhere without opening ports.
services:
tailscale:
image: tailscale/tailscale:latest
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
- SYS_MODULE
volumes:
- /dev/net/tun:/dev/net/tun
- tailscale_state:/var/lib/tailscale
environment:
- TS_AUTHKEY=${TAILSCALE_AUTH_KEY}
- TS_ROUTES=192.168.1.0/24 # Advertise your home subnet
- TS_ACCEPT_DNS=false
With subnet routing enabled, you can access all homelab services from your phone or laptop via Tailscale IPs — no Cloudflare Tunnel, no port forwarding.
Replaces: WireGuard self-hosted setup (Tailscale is WireGuard under the hood, just with automatic key management)
Layer 10: Automation
n8n or Activepieces
Workflow automation to connect all your services. Trigger actions when files are added to Nextcloud, send Telegram alerts when services go down, automate routine tasks.
services:
n8n:
image: n8nio/n8n:latest
restart: unless-stopped
ports:
- "5678:5678"
volumes:
- n8n_data:/home/node/.n8n
environment:
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
Common homelab automations:
- Uptime Kuma → Telegram: custom alert formatting
- Nextcloud file upload → trigger Immich sync
- Sonarr/Radarr download complete → Jellyfin library scan notification
- Weekly backup verification: ping Uptime Kuma heartbeat after backup script succeeds
Complete Hardware Sizing Guide
| Use Case | CPU | RAM | Storage | Power Draw |
|---|---|---|---|---|
| Minimal (3–5 services) | 4-core | 8 GB | 120 GB SSD + NAS | ~35W |
| Standard homelab (10–15 services) | 6–8 core | 16 GB | 256 GB SSD + 4 TB HDD | ~45–65W |
| Full stack (20+ services + Plex/Jellyfin 4K) | 8–12 core | 32 GB | 512 GB NVMe + 8+ TB | ~65–120W |
| Mini PC (N100/N305) | 4 core | 16 GB | 500 GB NVMe | ~10–15W |
2026 popular hardware: Intel N100/N305 mini PCs (Beelink EQ12, Minisforum N305) are the efficiency-per-dollar winners for homelabs under 15 services. HP EliteDesk mini PCs (used, $80–150) offer excellent value. Older Xeon workstations give CPU headroom for transcoding but use significantly more power.
The Full Stack: Monthly Cost
| Replaced Service | Homelab Equivalent | Monthly Savings |
|---|---|---|
| Google Photos (2 TB) | Immich | $3–10 |
| Google Workspace (1 user) | Nextcloud | $14 |
| Netflix | Jellyfin | $23 |
| Bitwarden Premium | Vaultwarden | $0.83 |
| Zapier (Pro) | n8n/Activepieces | $49 |
| Better Stack (100 monitors) | Uptime Kuma | $20 |
| Plex Pass | Jellyfin | $5.83 |
| Infrastructure (VPS or home server) | Hetzner VPS or $200 mini PC | -$15 |
| Net monthly savings | ~$110–130/month |
That's $1,320–$1,560/year in subscription savings from a homelab that takes 2–3 hours/month to maintain.
Getting Started: Recommended Build Order
Building everything at once is overwhelming. This sequence minimizes dead ends:
- Week 1: OS + Docker + Portainer + Nginx Proxy Manager + Uptime Kuma
- Week 2: Pi-hole (local DNS) + Vaultwarden
- Week 3: Nextcloud (file sync on one device first) + Immich
- Week 4: Jellyfin + one *arr service
- Month 2: Authentik SSO + Tailscale + automation (n8n or Activepieces)
- Ongoing: Add services as they replace specific subscriptions
Start monitoring (Uptime Kuma) before adding services — you'll want visibility into what breaks during setup.
Browse all self-hosted alternatives at OSSAlt. Related: Uptime Kuma monitoring guide, Jellyfin vs Plex comparison, Dify for AI workflows.