Homelab Production Deployment & Operations Guide
Comprehensive guide for deploying, configuring, maintaining, and updating the homelab stack on your production Ubuntu Server (HP ProDesk 600 G6 Tower, Intel Core i5-10400, 32GB RAM).
1. System Architecture & The 4 Core Layers
To keep operations intuitive and maintenance frictionless, the homelab ecosystem is composed of four distinct tools, each with one specific responsibility:
| Layer | Technology | Primary Responsibility |
|---|---|---|
| 1. The Vault | HP ProDesk Ubuntu Server | Hosts persistent storage, media engines (Immich, Navidrome, Calibre-Web), and intake bots behind a zero-open-ports firewall. |
| 2. The Secret Entrance | Tailscale Mesh (100.x.y.z) |
Point-to-point encrypted WireGuard mesh connecting your phone and laptop directly to your server from anywhere with MagicDNS. |
| 3. The Privacy Mask | ProtonVPN (WireGuard) | Masks your home IP address: strictly containerized on the server (gluetun) for torrents, and on client devices for web privacy. |
| 4. The Name Tag & SSL | Cloudflare (devsaddress.net) |
Provides human-friendly domain names and automated wildcard SSL certificates via DNS-01 API with zero open router ports. |
Master 4-Stage Deployment Roadmap
STAGE 1: Laptop Preparation (Current Stage)
└── Verify client mesh (laptop & phone pinging)
└── (Optional) Link docs.devsaddress.net to Cloudflare Pages
└── Draft Gluetun + ProtonVPN compose configuration
STAGE 2: Server Hardware Day (When ProDesk Arrives)
└── Install Ubuntu Server on HP ProDesk
└── Join Tailscale mesh with 1-liner: sudo tailscale up --hostname=homelab --ssh
└── Verify remote terminal access from laptop: ssh dev@homelab
STAGE 3: Deploy the Media Stack
└── Clone repository & configure production .env
└── Provision directories: make prod-prep
└── Launch 24/7 stack: make up (Immich, Navidrome, Audiobookshelf, Calibre-Web, Bots)
STAGE 4: Domain SSL & Protected Torrents
└── Configure Cloudflare DNS API token in .env for *.devsaddress.net
└── Add ProtonVPN WireGuard credentials to Gluetun container
└── Verify torrent kill switch and IP masking (make vpn-ip)
2. Hardware & OS Prerequisites
Hardware Requirements
- Platform: Ubuntu Server 24.04 / 22.04 LTS (x86_64).
- Storage Devices:
- Fast NVMe / SATA SSD: Hosts
/(OS) and/srv/docker/(container configs, SQLite/PostgreSQL databases, Valkey cache, AI model weights). - Bulk Storage:
/srv/storage/(media libraries, downloads, intake drop-chutes, restricted partition). Starts on SSD; later migrates to dedicated 3.5" HDD. - GPU: Intel UHD Graphics 630 (
/dev/dri/renderD128) for hardware-accelerated video transcoding (QuickSync).
Host Package Prerequisites
Before cloning, ensure the host system has Docker, Docker Compose, Git, Python 3, and FFmpeg:
# 1. Update apt repositories
sudo apt update && sudo apt upgrade -y
# 2. Install core utilities & media tools
sudo apt install -y curl wget git nano ffmpeg python3 python3-venv
# 3. Install official Docker CE and Compose plugin
curl -fsSL https://get.docker.com | sh
# 4. Add your user to the docker group (avoids sudo for container commands)
sudo usermod -aG docker $USER
# 5. Install Tailscale & join mesh
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --hostname=homelab --advertise-exit-node --ssh
[!IMPORTANT] Group Membership Refresh: After running
usermod -aG docker $USER, log out and SSH back in for group permissions to take effect. Verify withdocker pswithoutsudo.
3. Initial Production Bootstrap
Follow these 5 steps to deploy the production stack from scratch:
Step 1: Pre-Create /srv Storage Roots
In Linux, /srv is owned by root:root by default. Pre-creating the roots and transferring ownership to your non-root user prevents Docker from auto-creating host directories as root:root (which causes Permission Denied in containers running as UID 1000):
Step 2: Clone Repository
Clone the repository into your home directory (e.g., ~/homelab):
Step 3: Initialize Production Environment (.env)
Generate the .env configuration file from the template:
Key variables to review in .env:
* PUID & PGID: Set to 1000 (check via id -u and id -g).
* TZ: Set to your local timezone (e.g. America/New_York).
* DATA_ROOT: /srv/docker (default).
* STORAGE_ROOT: /srv/storage (default).
* DISCORD_BOT_TOKEN & DISCORD_GUILD_ID: Production Discord bot credentials.
* TELEGRAM_BOT_TOKEN & TELEGRAM_ALLOWED_USER_IDS: Private Telegram intake credentials.
* DB_PASSWORD: Strong database password for Immich PostgreSQL.
Step 4: Run Production Provisioning (make prod-prep)
Run the automated preparation target:
This single command:
1. Provisions all media library directories (/srv/storage/media/{music,audiobooks,podcasts,books,photos,video}).
2. Provisions intake chutes (/srv/storage/downloads/intake/ and /srv/storage/downloads/scratch/).
3. Provisions the restricted partition (/srv/storage/restricted/).
4. Provisions SSD container database paths (/srv/docker/{navidrome,calibre-web,audiobookshelf,immich}).
5. Automatically initializes and seeds the Calibre SQLite database (metadata.db) under /srv/storage/media/books.
Step 5: Start the 24/7 Core Stack
Pull production container images and launch the stack in detached mode:
Verify service status and healthchecks:
All core containers (audiobookshelf, calibre-web, navidrome, immich-server, immich-postgres, immich-valkey, discord-bot, telegram-bot) should display status healthy or running.
4. Day-2 Operations & Update Workflows
Server-Side Update (make update)
When you push bug fixes, documentation, or configuration changes to the GitHub repository, update the running server with a single command:
What make update does automatically:
1. Fetches and merges the latest git commits (git pull origin main).
2. Pulls updated container images from GHCR and Docker Hub (docker compose pull).
3. Rebuilds custom bot container images if code changed (docker compose build).
4. Restarts only the containers whose configuration or images changed, preserving uptime on untouched services (docker compose up -d --remove-orphans).
Laptop-to-Server Remote Deploy (make deploy)
You don't even need to SSH into the server to deploy updates. From your laptop (over Tailscale):
# Deploys to dev@homelab (or specify host=user@ip)
make deploy
# Or specify a custom host or Tailscale node:
make deploy host=dev@homelab
5. On-Demand Immich Machine Learning
To maintain the Zero-Idle resource profile (~1.3 GB total RAM across the 24/7 stack), facial recognition and CLIP search ML models are completely dormant until invoked:
- Activate ML Sidecar for Photo Batch:
Spawns
immich-machine-learning. Immich will process newly uploaded photo albums and facial clustering. - Dismantle ML Sidecar: Stops and removes the container, restoring the zero-idle baseline.
6. Backups & Disaster Recovery
Because database and state data (/srv/docker) are strictly separated from heavy media files (/srv/storage), fast daily backups take seconds and require very little storage space.
What to Back Up Daily
- Repository & Secrets:
~/homelab/.envandconfig/ - Container State & Databases:
/srv/docker/ immich/postgres: Immich database and VectorChord embeddingsnavidrome/: Subsonic SQLite library and playlist stateaudiobookshelf/: Playback progress, user bookmarks, and metadatacalibre-web/: E-reader sync states
Recommended Backup Script (Cron / Borg / Restic)
#!/bin/bash
BACKUP_DEST="/mnt/backup/homelab-$(date +%Y%m%d)"
mkdir -p "$BACKUP_DEST"
# 1. Back up .env secrets
cp ~/homelab/.env "$BACKUP_DEST/.env"
# 2. Snapshot /srv/docker (stop DB briefly or use postgres pg_dump)
rsync -aAX --delete /srv/docker "$BACKUP_DEST/"
echo "✅ Backup complete at $BACKUP_DEST"
7. Future Migration to Dedicated HDD Storage
When you add a 3.5" bulk HDD (e.g. 4TB–16TB WD Red / Seagate IronWolf) to your HP ProDesk, moving storage off the SSD requires zero changes to Docker or application configurations:
- Partition & Format the New HDD:
- Copy Existing Media & Data:
- Mount HDD to
/srv/storagein/etc/fstab: - Restart Containers: All containers continue running without knowing their storage moved to a high-capacity physical HDD!