Raspberry Pi 4 - Origin Homelab¶
Status: Retired (December 2025 - replaced by HP EliteDesk 800 G4 + Proxmox)
Overview¶
| Property | Value |
|---|---|
| Device | Raspberry Pi 4 Model B |
| RAM | 4 GB |
| OS | Raspberry Pi OS Lite 64-bit, later DietPi |
| IP Address | 192.168.0.102 (static) |
| Active | August 2024 - December 2025 |
| Purpose | All-in-one home server: media, DNS, monitoring, sync |
Hardware¶
| Component | Detail |
|---|---|
| Board | Raspberry Pi 4 Model B (4 GB) |
| Boot drive | USB SSD (OS + Docker volumes) |
| Storage | 2x USB HDD merged via MergerFS |
| Power | Powered USB hub (stable HDD power delivery) |
| Network | Gigabit Ethernet to TP-Link Archer C6 |
Storage¶
Two external HDDs were combined into a single unified volume using MergerFS:
/mnt/hdd1 - USB HDD 1
/mnt/hdd2 - USB HDD 2
/mnt/merged - MergerFS union mount (used by all services)
Network shares were exposed via Samba (SMB) for Windows and cross-device access.
This MergerFS setup was later carried over to the Proxmox migration and is still in use today (scaled to 4 HDDs, 8.1 TB usable).
Services¶
All services ran as Docker Compose stacks. Management was handled via Portainer and Dockge.
Media¶
| Service | Description |
|---|---|
| Jellyfin | Media server |
| Plex | Media server (ran alongside Jellyfin during testing) |
| Sonarr | TV show automation |
| Radarr | Movie automation |
| Prowlarr | Indexer management |
| Overseerr | Media request management |
| Bazarr | Subtitle management |
| qBittorrent | Torrent client |
| Tautulli | Plex statistics |
| Readarr | Book automation |
Network and DNS¶
| Service | Description |
|---|---|
| AdGuard Home | DNS-level ad blocking and filtering |
| Tailscale | Secure remote access (replaced Cloudflare Tunnel) |
| Nginx Proxy Manager | Reverse proxy (installed, not actively configured) |
Monitoring and Management¶
| Service | Description |
|---|---|
| Uptime Kuma | Service uptime monitoring |
| Scrutiny | HDD S.M.A.R.T. health monitoring |
| Dozzle | Real-time Docker log viewer |
| Watchtower | Automatic container image updates |
| Portainer | Container management UI |
| Dockge | Docker Compose stack manager |
| Homepage | Self-hosted dashboard |
Sync and Books¶
| Service | Description |
|---|---|
| Syncthing | Cross-device file synchronization |
| Calibre-Web (automated) | E-book library management |
Automation¶
- Watchtower handled automatic Docker image updates on a schedule
- Cron ran a bare-metal backup script every 14 days
- Ansible and Semaphore were installed on the desktop PC for automation experiments but never actively integrated with the Pi
Networking¶
- Static IP:
192.168.0.102 - Remote access: Tailscale (replaced Cloudflare Tunnel - simpler, no open ports)
- AdGuard Home handled LAN-wide DNS filtering (later moved to its own dedicated LXC on Proxmox)
Backup¶
Backups ran via a custom bash script on a 14-day cron schedule. The script used rsync to back up Docker volumes and the home directory to a second HDD (/mnt/hdd2/backup), keeping the last 2 snapshots.
Key design decisions:
- Containers with databases were stopped before backup - ensures consistent state (no partial DB writes)
- trap cleanup EXIT - if the script failed mid-run, containers were automatically restarted
- sync + cache drop - filesystem cache flushed before and after for clean state on a memory-constrained device
- set -euo pipefail - script aborts on any error, unset variable, or pipe failure
#!/bin/bash
set -euo pipefail
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
# === CONFIG ===
BACKUP_BASE="/mnt/hdd2/backup"
DATE=$(date +%Y-%m-%d)
BACKUP_DIR="$BACKUP_BASE/$DATE"
LOG_FILE="$BACKUP_DIR/backup.log"
# Containers with databases to stop
DB_CONTAINERS=("radarr" "sonarr" "jellyfin" "calibre-web-automated" "prowlarr" "bazarr" "qbittorrent")
# === LOGGING ===
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# === CLEANUP ON EXIT ===
cleanup() {
if [ $? -ne 0 ]; then
log "ERROR: Script failed, starting all containers..."
for c in "${DB_CONTAINERS[@]}"; do
docker start "$c" 2>/dev/null || true
done
fi
}
trap cleanup EXIT
mkdir -p "$BACKUP_DIR"
# === STOP DATABASE CONTAINERS ===
log "=== Stopping database containers ==="
for c in "${DB_CONTAINERS[@]}"; do
if docker ps -q -f name="^${c}$" | grep -q .; then
docker stop "$c" || log "WARNING: Failed to stop $c"
fi
done
sleep 5
sync
# === BACKUPS ===
rsync -aAXHv --delete --stats /srv/docker/ "$BACKUP_DIR/docker/" >> "$BACKUP_DIR/rsync_docker.log" 2>&1
rsync -aAXHv --delete --stats /home/nex/ "$BACKUP_DIR/nex_home/" >> "$BACKUP_DIR/rsync_home.log" 2>&1
# === CLEAR SYSTEM CACHE ===
sync
echo 3 > /proc/sys/vm/drop_caches
# === START DATABASE CONTAINERS ===
for c in "${DB_CONTAINERS[@]}"; do
docker start "$c" || log "WARNING: Failed to start $c"
done
sleep 10
# === DOCKER CLEANUP ===
docker image prune -a -f
docker volume prune -f
docker network prune -f
# === CLEANUP OLD BACKUPS (keep last 2) ===
cd "$BACKUP_BASE"
ls -1t | tail -n +3 | xargs -r rm -rf
This approach (rsync + manual container lifecycle) was replaced by Restic on Proxmox, which provides deduplication, encryption, and cloud offload to Backblaze B2 without requiring service downtime.
Why I Migrated to Proxmox¶
By late 2025 the Pi was running at its limits - 4 GB RAM with 20+ containers, no hardware transcoding, all services on a single OS with no isolation. The main pain points:
- No hardware transcoding - Jellyfin ran software-only on the Pi's CPU, causing buffering on high-bitrate content
- Resource contention - all services competed for the same 4 GB RAM with no isolation
- Single point of failure - one bad container update or config change could affect every service
- USB storage reliability - HDDs on a powered hub occasionally had mount issues that required manual recovery
- Limited upgrade path - adding more RAM or storage to a Pi 4 is not possible
In December 2025, the Pi was replaced by an HP EliteDesk 800 G4 (Intel i5-8400, 32 GB RAM) running Proxmox VE 9.1. Every service migrated to dedicated LXC containers, gaining proper isolation, GPU passthrough for Jellyfin, and reliable ZFS-backed storage.
The Pi itself was the proof of concept. Everything learned there - MergerFS, the arr stack, AdGuard, Tailscale - was carried forward to the current setup.
Lessons Learned¶
- MergerFS is the right tool for multi-HDD pooling without RAID - simple, reliable, still in use at larger scale
- Tailscale beats Cloudflare Tunnel for private access - no open ports, works transparently, zero config
- Watchtower is risky in production - automatic updates caused at least one broken stack; replaced with manual updates via Komodo in the Proxmox era
- USB HDDs need a powered hub - bus-powered drives on a Pi cause random disconnects under load
- Overseer/Jellyfin without hardware transcoding is painful - transcoding on Pi 4 CPU maxed it out at 1 stream
- Port mapping gets unmanageable fast - 20+ services with manually tracked ports pushed me toward a reverse proxy; this was solved properly with Pangolin on the VPS in the Proxmox era