# Wave 2 Migration Results - docker-vpn (VM 121 → LXC 221 arr-stack) **Date**: 2025-12-05 **Status**: **SUCCESSFUL** **Migration Time**: ~2 hours --- ## Summary Successfully migrated and restructured docker-vpn VM (121) to arr-stack LXC (221). The migration involved a significant architecture simplification - eliminating the Mullvad VPN entirely since only Usenet is used (SSL to Usenet provider is sufficient, no torrents). Additionally replaced Overseerr with Jellyseerr for native Jellyfin support. --- ## Migration Details ### Source (VM 121 - docker-vpn) - **OS**: Ubuntu (in VM) - **Services**: Sonarr, Radarr, Readarr, Overseerr, SABnzbd, Mullvad VPN - **Architecture**: All traffic routed through Mullvad VPN container - **Complexity**: High (VPN routing, multiple network namespaces) ### Destination (LXC 221 - arr-stack) - **OS**: Ubuntu 20.04 LTS (privileged LXC) - **Resources**: 2 cores, 4GB RAM, 32GB disk - **IP**: 10.10.0.221 - **Services**: Sonarr, Radarr, Readarr, Jellyseerr, SABnzbd - **Architecture**: Direct network access (no VPN) - **Complexity**: Low (standard Docker containers) --- ## Architecture Changes ### Before (docker-vpn) ``` Internet ↓ Mullvad VPN Container ↓ (all traffic tunneled) ├─ Sonarr ├─ Radarr ├─ Readarr ├─ Overseerr └─ SABnzbd ``` ### After (arr-stack) ``` Internet ↓ (direct, SSL encrypted to Usenet) ├─ Sonarr ├─ Radarr ├─ Readarr ├─ Jellyseerr (replaced Overseerr) └─ SABnzbd ``` ### Key Decision: VPN Elimination **Rationale**: - Only using Usenet (not torrents) - Usenet providers support SSL encryption - SSL to Usenet provider provides sufficient privacy - VPN added complexity without meaningful benefit - Simplified troubleshooting and maintenance --- ## Technical Implementation ### LXC Configuration ``` # /etc/pve/lxc/221.conf arch: amd64 cores: 2 features: nesting=1,keyctl=1 hostname: arr-stack memory: 4096 net0: name=eth0,bridge=vmbr0,gw=10.10.0.1,ip=10.10.0.221/24,type=veth ostype: ubuntu rootfs: local-lvm:vm-221-disk-0,size=32G swap: 512 lxc.apparmor.profile: unconfined ``` ### Docker Compose ```yaml services: sonarr: image: linuxserver/sonarr:latest container_name: sonarr ports: ["8989:8989"] volumes: - ./config/sonarr:/config - /mnt/media:/media security_opt: [apparmor=unconfined] restart: unless-stopped radarr: image: linuxserver/radarr:latest container_name: radarr ports: ["7878:7878"] volumes: - ./config/radarr:/config - /mnt/media:/media security_opt: [apparmor=unconfined] restart: unless-stopped readarr: image: ghcr.io/hotio/readarr:latest container_name: readarr ports: ["8787:8787"] volumes: - ./config/readarr:/config - /mnt/media:/media security_opt: [apparmor=unconfined] restart: unless-stopped jellyseerr: image: fallenbagel/jellyseerr:latest container_name: jellyseerr ports: ["5055:5055"] volumes: - ./config/jellyseerr:/app/config security_opt: [apparmor=unconfined] restart: unless-stopped sabnzbd: image: linuxserver/sabnzbd:latest container_name: sabnzbd ports: ["8080:8080"] volumes: - ./config/sabnzbd:/config - ./downloads:/downloads - /mnt/media:/media security_opt: [apparmor=unconfined] restart: unless-stopped ``` ### CIFS Mount ```fstab //10.10.0.35/media /mnt/media cifs vers=3.0,uid=0,credentials=/root/.smbcredentials 0 0 ``` --- ## Issues Encountered & Solutions ### Issue 1: linuxserver.io Registry (lscr.io) Pull Failures **Problem**: `no matching manifest for linux/amd64` errors from lscr.io registry **Solution**: Switched to Docker Hub images directly (`linuxserver/sonarr` instead of `lscr.io/linuxserver/sonarr`) ### Issue 2: Readarr Image Not Available **Problem**: linuxserver/readarr:develop tag not available for amd64 **Solution**: Switched to hotio image (`ghcr.io/hotio/readarr:latest`) ### Issue 3: Jellyseerr Tag Validation Error **Problem**: Radarr rejecting requests with "Label: Allowed characters a-z, 0-9 and -" **Cause**: Jellyseerr sending tags with invalid characters to Radarr **Solution**: Disabled tags in Jellyseerr Radarr integration settings --- ## Data Migration ### Configs Migrated - **Sonarr**: ~1.4GB (database, MediaCover cache, backups) - **Radarr**: ~1.6GB (database, MediaCover cache, backups) - **Readarr**: ~88MB (database, backups) - **Overseerr**: ~7.7MB (database, settings) - Not used, replaced with Jellyseerr ### Fresh Configuration Required - **SABnzbd**: Fresh install (user configured Usenet provider) - **Jellyseerr**: Fresh install (connected to Jellyfin) --- ## Validation Results | Service | Port | Status | Test | |---------|------|--------|------| | Sonarr | 8989 | HTTP 200 | Database loaded, shows configured | | Radarr | 7878 | HTTP 200 | Database loaded, movie requests working | | Readarr | 8787 | HTTP 200 | Database loaded, shows configured | | Jellyseerr | 5055 | HTTP 307 | Connected to Jellyfin, requests working | | SABnzbd | 8080 | HTTP 303 | Configured with Usenet provider | | CIFS Mount | - | Working | Media accessible in containers | --- ## Resource Comparison ### Before (VM 121) - **Memory**: Full VM overhead (~1-2GB for OS) - **Disk**: Larger allocation for VM image - **Complexity**: VPN routing, multiple network namespaces - **Maintenance**: VPN updates, connection monitoring ### After (LXC 221) - **Memory**: ~100MB LXC overhead - **Disk**: 32GB (minimal) - **Complexity**: Standard Docker containers - **Maintenance**: Standard container updates only ### Efficiency Gains - **~1.5GB RAM saved** (VM overhead eliminated) - **Simplified networking** (no VPN routing) - **Reduced attack surface** (fewer services) - **Faster boot time** (LXC vs VM) --- ## NPM/Reverse Proxy Updates Updated Nginx Proxy Manager entries to point to new IP: - sonarr.manticorum.com → 10.10.0.221:8989 - radarr.manticorum.com → 10.10.0.221:7878 - readarr.manticorum.com → 10.10.0.221:8787 - jellyseerr.manticorum.com → 10.10.0.221:5055 (new, replaces overseerr) - sabnzbd.manticorum.com → 10.10.0.221:8080 --- ## Rollback Capability - **VM 121 preserved**: Can be restarted if issues arise - **Rollback time**: <5 minutes - **Recommendation**: Keep VM 121 stopped for 48 hours, then decommission --- ## Key Learnings ### 1. VPN Complexity Often Unnecessary For Usenet-only setups, VPN adds complexity without meaningful benefit. SSL to the Usenet provider is sufficient. ### 2. Image Registry Issues lscr.io can have availability issues. Docker Hub images work as fallback. ### 3. Application Substitution Jellyseerr is a drop-in replacement for Overseerr with native Jellyfin support - worth the switch if using Jellyfin. ### 4. Tag/Label Validation When connecting Jellyseerr to arr apps, be careful with tag configurations - invalid characters cause silent failures. --- ## Next Steps ### Immediate - [x] Configure SABnzbd with Usenet provider - [x] Connect arr apps to new SABnzbd - [x] Update NPM reverse proxy entries - [x] Test movie/show requests through Jellyseerr ### After 48 Hours - [ ] Decommission VM 121 (docker-vpn) - [ ] Clean up local migration temp files (`/tmp/arr-config-migration/`) --- ## Files Created/Modified ### On LXC 221 - `/opt/arr-stack/docker-compose.yml` - `/opt/arr-stack/config/` (all service configs) - `/root/.smbcredentials` - `/etc/fstab` (CIFS mount) ### Documentation Updated - `vm-management/lxc-migration-plan.md` - Wave 2 status - `networking/server-inventory.md` - Added arr-stack entry - `vm-management/wave2-migration-results.md` - This file --- **Status**: **Wave 2 Complete - Ready for Wave 3** **Contact**: Cal Corum (cal.corum@gmail.com)