- Add LXC migration plan and quick-start guide - Add wave 1 and wave 2 migration results - Add lxc-docker-create.sh for container creation - Add fix-docker-apparmor.sh for AppArmor issues - Add comprehensive LXC migration guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.6 KiB
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
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
//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
- Configure SABnzbd with Usenet provider
- Connect arr apps to new SABnzbd
- Update NPM reverse proxy entries
- 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 statusnetworking/server-inventory.md- Added arr-stack entryvm-management/wave2-migration-results.md- This file
Status: Wave 2 Complete - Ready for Wave 3 Contact: Cal Corum (cal.corum@gmail.com)