#!/bin/bash # ===================================================================== # Nginx Proxy Manager LXC Setup + Migration from HA Addon # ===================================================================== # Run this script on the Proxmox host (HAL-HOST / 10.0.0.240) as root. # # Network: # - Proxmox host: 10.0.0.240 # - HAOS VM: 10.0.0.55 # - AdGuard LXC: 10.0.0.224 (CT 120) # - Guacamole LXC: 10.0.0.225 (CT 121) # - NPM LXC: 10.0.0.226 (CT 122) # ===================================================================== set -euo pipefail CT_ID="122" CT_NAME="npm" CT_IP="10.0.0.226/24" CT_GW="10.0.0.254" CT_STORAGE="local-lvm" CT_TEMPLATE="local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst" CT_MEMORY=512 CT_SWAP=256 CT_CORES=2 CT_DISK="4" BRIDGE="vmbr0" HAOS_IP="10.0.0.55" ADDON_SLUG="a0d7b954_nginxproxymanager" echo "============================================" echo " Nginx Proxy Manager LXC Setup + Migration" echo " Container ID: ${CT_ID}" echo " IP Address: ${CT_IP}" echo "============================================" # --- Template --- if ! pveam list local | grep -q "debian-12-standard"; then echo "[1/9] Downloading Debian 12 template..." pveam download local debian-12-standard_12.12-1_amd64.tar.zst else echo "[1/9] Debian 12 template already available" fi # --- Create container --- echo "[2/9] Creating LXC container ${CT_ID}..." pct create "${CT_ID}" "${CT_TEMPLATE}" \ --hostname "${CT_NAME}" \ --memory "${CT_MEMORY}" \ --swap "${CT_SWAP}" \ --cores "${CT_CORES}" \ --rootfs "${CT_STORAGE}:${CT_DISK}" \ --net0 "name=eth0,bridge=${BRIDGE},ip=${CT_IP},gw=${CT_GW},firewall=0" \ --nameserver "10.0.0.224" \ --onboot 1 \ --unprivileged 1 \ --features "nesting=1,keyctl=1" \ --startup "order=2,up=10" # --- AppArmor fix --- echo "[3/9] Applying AppArmor fix for Docker compatibility..." CT_CONF="/etc/pve/lxc/${CT_ID}.conf" if ! grep -q "lxc.apparmor.profile" "${CT_CONF}" 2>/dev/null; then echo "lxc.apparmor.profile: unconfined" >> "${CT_CONF}" fi pct start "${CT_ID}" echo "[4/9] Waiting for container to start..." sleep 5 # --- Install Docker --- echo "[5/9] Installing Docker..." pct exec "${CT_ID}" -- bash -c ' apt-get update -qq apt-get install -y -qq ca-certificates curl gnupg install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list apt-get update -qq apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl enable docker systemctl start docker ' # --- Docker Compose --- echo "[6/9] Creating NPM Docker Compose configuration..." pct exec "${CT_ID}" -- bash -c " mkdir -p /opt/npm/data /opt/npm/letsencrypt cat > /opt/npm/docker-compose.yml << 'DCEOF' services: npm: image: jc21/nginx-proxy-manager:latest container_name: npm restart: unless-stopped ports: - '80:80' - '443:443' - '81:81' volumes: - /opt/npm/data:/data - /opt/npm/letsencrypt:/etc/letsencrypt environment: TZ: Australia/Melbourne DB_SQLITE_FILE: /data/database.sqlite healthcheck: test: ['CMD', '/usr/bin/check-health'] interval: 10s timeout: 3s DCEOF " # ================================================================= # DATA MIGRATION # ================================================================= echo "[7/9] Data migration from HA addon..." MIGRATED=false # --- Try SSH first --- for PORT in 22222 22; do if ssh -o ConnectTimeout=3 -o StrictHostKeyChecking=no -p ${PORT} \ root@${HAOS_IP} "test -d /addon_data/${ADDON_SLUG}" 2>/dev/null; then echo " SSH connected (port ${PORT}), copying data..." TMPDIR=$(mktemp -d) scp -P ${PORT} -r -o StrictHostKeyChecking=no \ root@${HAOS_IP}:/addon_data/${ADDON_SLUG}/ "${TMPDIR}/" 2>/dev/null ADDON_DATA="${TMPDIR}/${ADDON_SLUG}" if [ -d "${ADDON_DATA}" ]; then DB_SRC=$(find "${ADDON_DATA}" -name "database.sqlite" -type f 2>/dev/null | head -1) [ -n "${DB_SRC}" ] && pct push "${CT_ID}" "${DB_SRC}" /opt/npm/data/database.sqlite && echo " DB migrated" CERT_DIR=$(find "${ADDON_DATA}" -type d -name "letsencrypt" 2>/dev/null | head -1) if [ -n "${CERT_DIR}" ]; then tar -czf "${TMPDIR}/c.tar.gz" -C "${CERT_DIR}" . 2>/dev/null pct push "${CT_ID}" "${TMPDIR}/c.tar.gz" /tmp/c.tar.gz pct exec "${CT_ID}" -- bash -c 'tar xzf /tmp/c.tar.gz -C /opt/npm/letsencrypt/ && rm /tmp/c.tar.gz' echo " Certs migrated" fi MIGRATED=true fi rm -rf "${TMPDIR}" break fi done # --- If SSH failed, interactive migration from HA terminal --- if [ "$MIGRATED" = false ]; then echo "" echo " SSH to HAOS failed." echo "" echo " ┌─────────────────────────────────────────────────┐" echo " │ To migrate your data, run this in the HA │" echo " │ Terminal & SSH addon (or HA console): │" echo " │ │" echo " │ cd /addon_data/${ADDON_SLUG}" echo " │ tar czf /tmp/npm-backup.tar.gz . │" echo " │ scp /tmp/npm-backup.tar.gz \\ │" echo " │ root@10.0.0.240:/tmp/npm-backup.tar.gz │" echo " │ │" echo " │ (Accept the host key if prompted, enter the │" echo " │ Proxmox root password when asked) │" echo " └─────────────────────────────────────────────────┘" echo "" read -r -p " Press Enter once you've copied the file (or type 'skip'): " RESPONSE if [ "${RESPONSE}" != "skip" ] && [ -f "/tmp/npm-backup.tar.gz" ]; then echo " Found /tmp/npm-backup.tar.gz, extracting..." TMPDIR=$(mktemp -d) tar xzf /tmp/npm-backup.tar.gz -C "${TMPDIR}/" 2>/dev/null || true DB_SRC=$(find "${TMPDIR}" -name "database.sqlite" -type f 2>/dev/null | head -1) if [ -n "${DB_SRC}" ]; then pct push "${CT_ID}" "${DB_SRC}" /opt/npm/data/database.sqlite echo " DB migrated" else echo " WARNING: database.sqlite not found in archive" echo " Archive contents:" find "${TMPDIR}" -maxdepth 3 -type f | head -20 fi CERT_DIR=$(find "${TMPDIR}" -type d -name "letsencrypt" 2>/dev/null | head -1) if [ -n "${CERT_DIR}" ]; then tar -czf "${TMPDIR}/c.tar.gz" -C "${CERT_DIR}" . 2>/dev/null pct push "${CT_ID}" "${TMPDIR}/c.tar.gz" /tmp/c.tar.gz pct exec "${CT_ID}" -- bash -c 'tar xzf /tmp/c.tar.gz -C /opt/npm/letsencrypt/ && rm /tmp/c.tar.gz' echo " Certs migrated" fi MIGRATED=true rm -rf "${TMPDIR}" rm -f /tmp/npm-backup.tar.gz elif [ "${RESPONSE}" != "skip" ]; then echo " File not found at /tmp/npm-backup.tar.gz" echo " You can migrate later — see instructions below." else echo " Skipping migration." fi fi # --- Start NPM --- echo "[8/9] Starting Nginx Proxy Manager..." pct exec "${CT_ID}" -- bash -c ' cd /opt/npm docker compose up -d echo "Waiting for NPM to initialise..." sleep 15 docker compose ps ' # --- Systemd service --- echo "[9/9] Creating systemd service for auto-start..." pct exec "${CT_ID}" -- bash -c ' cat > /etc/systemd/system/npm.service << "SVCEOF" [Unit] Description=Nginx Proxy Manager Docker Requires=docker.service After=docker.service [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/npm ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down TimeoutStartSec=120 [Install] WantedBy=multi-user.target SVCEOF systemctl daemon-reload systemctl enable npm.service ' CT_IP_CLEAN=$(echo "${CT_IP}" | cut -d'/' -f1) echo "" echo "============================================" echo " Nginx Proxy Manager LXC setup complete!" echo "============================================" echo "" echo " Admin UI: http://${CT_IP_CLEAN}:81" echo " HTTP: ${CT_IP_CLEAN}:80" echo " HTTPS: ${CT_IP_CLEAN}:443" echo "" if [ "$MIGRATED" = true ]; then echo " Data migrated from HA addon." echo " Login with your existing NPM credentials." else echo " Default login (fresh install):" echo " Email: admin@example.com" echo " Password: changeme" echo " >>> CHANGE THIS IMMEDIATELY <<<" echo "" echo " TO MIGRATE DATA LATER:" echo " From HA Terminal & SSH addon:" echo " cd /addon_data/${ADDON_SLUG}" echo " tar czf /tmp/npm-backup.tar.gz ." echo " scp /tmp/npm-backup.tar.gz root@10.0.0.240:/tmp/" echo "" echo " Then on Proxmox:" echo " pct exec ${CT_ID} -- bash -c 'cd /opt/npm && docker compose down'" echo " mkdir /tmp/npm_ex && tar xzf /tmp/npm-backup.tar.gz -C /tmp/npm_ex/" echo " DB=\$(find /tmp/npm_ex -name database.sqlite | head -1)" echo " pct push ${CT_ID} \$DB /opt/npm/data/database.sqlite" echo " pct exec ${CT_ID} -- bash -c 'cd /opt/npm && docker compose up -d'" fi echo "" echo " NEXT STEPS:" echo " 1. Verify proxy hosts at http://${CT_IP_CLEAN}:81" echo " 2. Update OPNsense port forwards (80/443):" echo " OLD: 10.0.0.55 -> NEW: ${CT_IP_CLEAN}" echo " 3. If certs didn't migrate, re-request via NPM" echo " 4. Stop HA NPM add-on after confirming" echo "============================================"