#!/bin/bash # ===================================================================== # AdGuard Home LXC Setup for Proxmox # ===================================================================== # Migrates AdGuard Home from a Home Assistant add-on to a dedicated # lightweight LXC container on Proxmox. This eliminates DNS dependency # on HA stability. # # 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) # # Since AdGuard has NO GUI export, this script includes steps to # migrate the config via SSH from the HAOS addon data directory. # ===================================================================== set -euo pipefail # --- Configuration --- CT_ID="120" CT_NAME="adguard" CT_IP="10.0.0.224/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=256 CT_SWAP=128 CT_CORES=1 CT_DISK="2" BRIDGE="vmbr0" HAOS_IP="10.0.0.55" ADDON_SLUG="a0d7b954_adguard" echo "============================================" echo " AdGuard Home LXC Setup" echo " Container ID: ${CT_ID}" echo " IP Address: ${CT_IP}" echo "============================================" # --- Check if template exists, download if not --- if ! pveam list local | grep -q "debian-12-standard"; then echo "[1/8] Downloading Debian 12 template..." pveam download local debian-12-standard_12.12-1_amd64.tar.zst else echo "[1/8] Debian 12 template already available" fi # --- Create the container --- echo "[2/8] 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 "1.1.1.1" \ --onboot 1 \ --start 1 \ --unprivileged 1 \ --features "nesting=1" \ --startup "order=1,up=10" echo "[3/8] Waiting for container to start..." sleep 5 # --- Install AdGuard Home inside the container --- echo "[4/8] Installing AdGuard Home..." pct exec "${CT_ID}" -- bash -c ' apt-get update -qq && apt-get upgrade -y -qq apt-get install -y -qq curl ca-certificates curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v systemctl enable AdGuardHome systemctl start AdGuardHome ' # --- Attempt to migrate config from HA addon --- echo "[5/8] Attempting to migrate config from HA addon..." echo " Trying SSH to HAOS at ${HAOS_IP}..." 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 addon data directory. Copying config..." # Stop AdGuard on new LXC before overwriting config pct exec "${CT_ID}" -- systemctl stop AdGuardHome # Copy the entire addon data directory TMPDIR=$(mktemp -d) scp -r -o StrictHostKeyChecking=no root@${HAOS_IP}:/addon_data/${ADDON_SLUG}/ "${TMPDIR}/" 2>/dev/null if [ -f "${TMPDIR}/${ADDON_SLUG}/AdGuardHome.yaml" ] || [ -f "${TMPDIR}/${ADDON_SLUG}/data/AdGuardHome.yaml" ]; then # Find the yaml config CONFIG_SRC=$(find "${TMPDIR}" -name "AdGuardHome.yaml" -type f | head -1) if [ -n "${CONFIG_SRC}" ]; then echo " Found config at: ${CONFIG_SRC}" # Copy config into the LXC pct push "${CT_ID}" "${CONFIG_SRC}" /opt/AdGuardHome/AdGuardHome.yaml # Update the bind address to listen on all interfaces pct exec "${CT_ID}" -- sed -i 's/address: .*/address: 0.0.0.0:80/' /opt/AdGuardHome/AdGuardHome.yaml # Copy filter data if it exists DATA_DIR=$(dirname "${CONFIG_SRC}")/data if [ -d "${DATA_DIR}" ]; then echo " Copying filter data, query logs, and stats..." for f in "${DATA_DIR}"/*; do [ -f "$f" ] && pct push "${CT_ID}" "$f" "/opt/AdGuardHome/data/$(basename "$f")" 2>/dev/null || true done fi MIGRATED=true echo " Config migration successful!" fi fi rm -rf "${TMPDIR}" if [ "$MIGRATED" = false ]; then echo " Could not locate AdGuardHome.yaml in addon data." echo " Will use default config instead." fi else echo " Could not SSH to HAOS at ${HAOS_IP}." echo " Make sure the Terminal & SSH addon is installed and" echo " SSH access is enabled (port 22222 or 22)." echo " Will use default config instead." fi # --- If migration failed, write default config --- if [ "$MIGRATED" = false ]; then echo "[6/8] Writing default AdGuard Home config..." pct exec "${CT_ID}" -- systemctl stop AdGuardHome sleep 2 pct exec "${CT_ID}" -- bash -c 'cat > /opt/AdGuardHome/AdGuardHome.yaml << "ADGEOF" http: pprof: port: 6060 enabled: false address: 0.0.0.0:80 session_ttl: 720h users: [] auth_attempts: 5 block_auth_min: 15 language: en theme: auto dns: bind_hosts: - 0.0.0.0 port: 53 ratelimit: 0 refuse_any: true upstream_dns: - https://dns.cloudflare.com/dns-query - https://dns.google/dns-query - 1.1.1.1 - 8.8.8.8 bootstrap_dns: - 1.1.1.1 - 8.8.8.8 upstream_mode: parallel cache_size: 4194304 cache_optimistic: true enable_dnssec: true max_goroutines: 300 serve_plain_dns: true hostsfile_enabled: true tls: enabled: false querylog: interval: 24h size_memory: 1000 enabled: true file_enabled: true statistics: interval: 168h enabled: true filters: - enabled: true url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt name: AdGuard DNS filter id: 1 - enabled: true url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt name: AdAway Default Blocklist id: 2 dhcp: enabled: false filtering: blocked_services: schedule: time_zone: Australia/Melbourne ids: [] safe_search: enabled: false blocking_mode: default parental_enabled: true safebrowsing_enabled: true filtering_enabled: true parental_block_host: family-block.dns.adguard.com safebrowsing_block_host: standard-block.dns.adguard.com log: enabled: true max_size: 100 max_age: 3 schema_version: 29 ADGEOF' else echo "[6/8] Config already migrated from HA, skipping default config." fi # --- Start AdGuard --- echo "[7/8] Starting AdGuard Home..." pct exec "${CT_ID}" -- systemctl start AdGuardHome sleep 3 pct exec "${CT_ID}" -- systemctl status AdGuardHome --no-pager -l | head -10 echo "[8/8] Testing DNS resolution..." pct exec "${CT_ID}" -- bash -c 'apt-get install -y -qq dnsutils 2>/dev/null && dig @127.0.0.1 google.com +short' 2>/dev/null || echo "(dig not available, but service is running)" CT_IP_CLEAN=$(echo "${CT_IP}" | cut -d'/' -f1) echo "" echo "============================================" echo " AdGuard Home LXC setup complete!" echo "============================================" echo "" echo " Web UI: http://${CT_IP_CLEAN}:80" echo " DNS: ${CT_IP_CLEAN}:53" echo "" if [ "$MIGRATED" = true ]; then echo " Config was migrated from HA addon." echo " Your filter lists, DNS rewrites, and" echo " parental controls should already be there." else echo " Using DEFAULT config (migration failed)." echo " Complete the setup wizard at http://${CT_IP_CLEAN}:80" echo "" echo " MANUAL CONFIG MIGRATION:" echo " Since there is no export option in AdGuard GUI," echo " copy the config manually via HA Terminal & SSH:" echo "" echo " # From the HAOS SSH terminal:" echo " scp /addon_data/${ADDON_SLUG}/AdGuardHome.yaml \\" echo " root@10.0.0.240:/tmp/adguard-config.yaml" echo "" echo " # Then on Proxmox (10.0.0.240):" echo " pct push ${CT_ID} /tmp/adguard-config.yaml \\" echo " /opt/AdGuardHome/AdGuardHome.yaml" echo " pct exec ${CT_ID} -- systemctl restart AdGuardHome" fi echo "" echo " NEXT STEPS:" echo " 1. Test: nslookup google.com ${CT_IP_CLEAN}" echo " 2. Update OPNsense DHCP DNS: 10.0.0.55 -> ${CT_IP_CLEAN}" echo " Services > DHCPv4 > [LAN] > DNS servers" echo " 3. Wait 24hrs, confirm stability" echo " 4. Stop HA AdGuard add-on" echo " 5. Optional: re-add HA integration -> ${CT_IP_CLEAN}" echo " 6. Optional: NPM proxy adguard.hideawaygaming.com.au" echo " -> http://${CT_IP_CLEAN}:80" echo "============================================"