325 lines
11 KiB
Bash
325 lines
11 KiB
Bash
#!/bin/bash
|
|
# =====================================================================
|
|
# Nginx Proxy Manager LXC Setup + Migration from HA Addon
|
|
# =====================================================================
|
|
# Migrates NPM from a Home Assistant add-on to a dedicated LXC
|
|
# container on Proxmox. Copies the SQLite database (all proxy hosts,
|
|
# users, settings) and Let's Encrypt certificates so you don't have
|
|
# to re-create anything.
|
|
#
|
|
# 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
|
|
# - OPNsense: 10.0.0.254
|
|
# - AdGuard LXC: 10.0.0.224 (CT 120)
|
|
# - Guacamole LXC: 10.0.0.225 (CT 121)
|
|
# - NPM LXC: 10.0.0.226 (CT 122)
|
|
#
|
|
# HA Addon slug: a0d7b954_nginxproxymanager
|
|
# Addon data path on HAOS: /addon_data/a0d7b954_nginxproxymanager/
|
|
#
|
|
# IMPORTANT: After migration, update your OPNsense port forwards
|
|
# (80/443) to point to 10.0.0.226 instead of 10.0.0.55.
|
|
# =====================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# --- Configuration ---
|
|
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"
|
|
NPM_DATA="/opt/npm/data"
|
|
|
|
echo "============================================"
|
|
echo " Nginx Proxy Manager LXC Setup + Migration"
|
|
echo " Container ID: ${CT_ID}"
|
|
echo " IP Address: ${CT_IP}"
|
|
echo "============================================"
|
|
|
|
# --- Check if template exists ---
|
|
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 the 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 \
|
|
--start 1 \
|
|
--unprivileged 1 \
|
|
--features "nesting=1,keyctl=1" \
|
|
--startup "order=2,up=10"
|
|
|
|
echo "[3/9] Waiting for container to start..."
|
|
sleep 5
|
|
|
|
# --- Install Docker inside the container ---
|
|
echo "[4/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
|
|
'
|
|
|
|
# --- Create Docker Compose config ---
|
|
echo "[5/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
|
|
"
|
|
|
|
# --- Attempt data migration from HA addon ---
|
|
echo "[6/9] Attempting data migration from HA addon..."
|
|
MIGRATED=false
|
|
|
|
if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@${HAOS_IP} "test -d /addon_data/${ADDON_SLUG}" 2>/dev/null; then
|
|
echo " Found NPM addon data. Copying..."
|
|
TMPDIR=$(mktemp -d)
|
|
|
|
# Copy entire addon data directory from HAOS
|
|
scp -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
|
|
echo " Addon data downloaded to ${TMPDIR}"
|
|
|
|
# --- Migrate SQLite database ---
|
|
# HA addon NPM may store the DB in various locations
|
|
DB_SRC=""
|
|
for candidate in \
|
|
"${ADDON_DATA}/database.sqlite" \
|
|
"${ADDON_DATA}/data/database.sqlite" \
|
|
"${ADDON_DATA}/nginx/database.sqlite"; do
|
|
if [ -f "$candidate" ]; then
|
|
DB_SRC="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -n "${DB_SRC}" ]; then
|
|
echo " Found database: ${DB_SRC}"
|
|
pct push "${CT_ID}" "${DB_SRC}" /opt/npm/data/database.sqlite
|
|
echo " Database migrated (proxy hosts, users, settings)"
|
|
else
|
|
echo " WARNING: database.sqlite not found in addon data"
|
|
# List what we did find for debugging
|
|
echo " Contents of addon data:"
|
|
find "${ADDON_DATA}" -maxdepth 3 -type f | head -30
|
|
fi
|
|
|
|
# --- Migrate Let's Encrypt certificates ---
|
|
CERTS_SRC=""
|
|
for candidate in \
|
|
"${ADDON_DATA}/letsencrypt" \
|
|
"${ADDON_DATA}/ssl" \
|
|
"${ADDON_DATA}/data/letsencrypt"; do
|
|
if [ -d "$candidate" ]; then
|
|
CERTS_SRC="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -n "${CERTS_SRC}" ]; then
|
|
echo " Found certificates: ${CERTS_SRC}"
|
|
# Tar up certs, push to LXC, extract
|
|
tar -czf "${TMPDIR}/certs.tar.gz" -C "${CERTS_SRC}" . 2>/dev/null
|
|
pct push "${CT_ID}" "${TMPDIR}/certs.tar.gz" /tmp/certs.tar.gz
|
|
pct exec "${CT_ID}" -- bash -c '
|
|
mkdir -p /opt/npm/letsencrypt
|
|
tar -xzf /tmp/certs.tar.gz -C /opt/npm/letsencrypt/
|
|
rm /tmp/certs.tar.gz
|
|
'
|
|
CERT_COUNT=$(find "${CERTS_SRC}" -name "*.pem" -o -name "*.crt" 2>/dev/null | wc -l)
|
|
echo " Certificates migrated (${CERT_COUNT} cert files)"
|
|
else
|
|
echo " No certificate directory found (will need re-issue)"
|
|
fi
|
|
|
|
# --- Migrate custom Nginx configs ---
|
|
for custom_dir in \
|
|
"${ADDON_DATA}/nginx/custom" \
|
|
"${ADDON_DATA}/data/nginx/custom"; do
|
|
if [ -d "$custom_dir" ] && [ "$(ls -A "$custom_dir" 2>/dev/null)" ]; then
|
|
echo " Found custom Nginx configs: ${custom_dir}"
|
|
tar -czf "${TMPDIR}/custom.tar.gz" -C "${custom_dir}" . 2>/dev/null
|
|
pct push "${CT_ID}" "${TMPDIR}/custom.tar.gz" /tmp/custom.tar.gz
|
|
pct exec "${CT_ID}" -- bash -c '
|
|
mkdir -p /opt/npm/data/nginx/custom
|
|
tar -xzf /tmp/custom.tar.gz -C /opt/npm/data/nginx/custom/
|
|
rm /tmp/custom.tar.gz
|
|
'
|
|
echo " Custom configs migrated"
|
|
break
|
|
fi
|
|
done
|
|
|
|
MIGRATED=true
|
|
fi
|
|
|
|
rm -rf "${TMPDIR}"
|
|
else
|
|
echo " Could not SSH to HAOS at ${HAOS_IP}."
|
|
echo " Make sure Terminal & SSH addon is installed."
|
|
echo ""
|
|
echo " MANUAL MIGRATION:"
|
|
echo " From HAOS SSH terminal, run:"
|
|
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 (10.0.0.240):"
|
|
echo " pct push ${CT_ID} /tmp/npm-backup.tar.gz /tmp/npm-backup.tar.gz"
|
|
echo " pct exec ${CT_ID} -- bash -c '"
|
|
echo " tar -xzf /tmp/npm-backup.tar.gz -C /opt/npm/data/"
|
|
echo " rm /tmp/npm-backup.tar.gz'"
|
|
fi
|
|
|
|
# --- Start NPM ---
|
|
echo "[7/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
|
|
'
|
|
|
|
# --- Create systemd service ---
|
|
echo "[8/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
|
|
'
|
|
|
|
# --- Verify ---
|
|
echo "[9/9] Verifying NPM is accessible..."
|
|
CT_IP_CLEAN=$(echo "${CT_IP}" | cut -d'/' -f1)
|
|
sleep 5
|
|
HTTP_CODE=$(pct exec "${CT_ID}" -- curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:81/ 2>/dev/null || echo "000")
|
|
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ]; then
|
|
echo " NPM admin panel responding (HTTP ${HTTP_CODE})"
|
|
else
|
|
echo " NPM may still be starting up (HTTP ${HTTP_CODE})"
|
|
echo " Give it another minute, then check http://${CT_IP_CLEAN}:81"
|
|
fi
|
|
|
|
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 was migrated from HA addon!"
|
|
echo " Your proxy hosts and SSL certs should be intact."
|
|
echo ""
|
|
echo " Default login (if DB migration succeeded):"
|
|
echo " Use your existing NPM credentials."
|
|
echo ""
|
|
echo " If login fails (schema mismatch), use defaults:"
|
|
echo " Email: admin@example.com"
|
|
echo " Password: changeme"
|
|
else
|
|
echo " Default login (fresh install):"
|
|
echo " Email: admin@example.com"
|
|
echo " Password: changeme"
|
|
echo " >>> CHANGE THIS IMMEDIATELY <<<"
|
|
fi
|
|
echo ""
|
|
echo " CRITICAL NEXT STEPS:"
|
|
echo ""
|
|
echo " 1. Verify all proxy hosts work at http://${CT_IP_CLEAN}:81"
|
|
echo ""
|
|
echo " 2. Update OPNsense port forwards:"
|
|
echo " Firewall > NAT > Port Forward"
|
|
echo " Change destination for ports 80/443:"
|
|
echo " OLD: 10.0.0.55 (HAOS)"
|
|
echo " NEW: ${CT_IP_CLEAN} (NPM LXC)"
|
|
echo ""
|
|
echo " 3. Update any DNS records pointing to the old IP"
|
|
echo " (all *.hideawaygaming.com.au should resolve"
|
|
echo " to your WAN IP, which OPNsense forwards)"
|
|
echo ""
|
|
echo " 4. If certs didn't migrate, re-request them:"
|
|
echo " NPM Admin > SSL Certificates > Add"
|
|
echo " Use DNS challenge or HTTP challenge"
|
|
echo ""
|
|
echo " 5. After confirming everything works:"
|
|
echo " Stop HA NPM add-on"
|
|
echo ""
|
|
echo " Docker management:"
|
|
echo " pct exec ${CT_ID} -- docker compose -f /opt/npm/docker-compose.yml logs -f"
|
|
echo " pct exec ${CT_ID} -- docker compose -f /opt/npm/docker-compose.yml restart"
|
|
echo "============================================"
|