#!/bin/bash # ===================================================================== # Apache Guacamole LXC Setup for Proxmox (Docker-based) # ===================================================================== # Migrates Guacamole from a Home Assistant add-on to a dedicated # LXC container on Proxmox. Guacamole was using 25% CPU and 9% RAM # inside the HAOS VM — this frees those resources. # # 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) # ===================================================================== set -euo pipefail # --- Configuration --- CT_ID="121" CT_NAME="guacamole" CT_IP="10.0.0.225/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=1024 CT_SWAP=256 CT_CORES=2 CT_DISK="8" BRIDGE="vmbr0" GUAC_DB_PASSWORD="$(openssl rand -hex 16 2>/dev/null || echo "ChangeMe_$(date +%s)")" echo "============================================" echo " Apache Guacamole LXC Setup" 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 (don't start yet - need AppArmor fix first) --- 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=3,up=15" # --- Apply AppArmor fix for Docker-in-LXC --- 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 inside the container --- 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 ' # --- Create Docker Compose config --- echo "[6/9] Creating Guacamole Docker Compose configuration..." pct exec "${CT_ID}" -- bash -c " mkdir -p /opt/guacamole cat > /opt/guacamole/docker-compose.yml << DCEOF services: guacd: image: guacamole/guacd:latest container_name: guacd restart: unless-stopped networks: - guac-net postgres: image: postgres:15-alpine container_name: guac-postgres restart: unless-stopped environment: POSTGRES_DB: guacamole_db POSTGRES_USER: guacamole_user POSTGRES_PASSWORD: ${GUAC_DB_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data - /opt/guacamole/initdb:/docker-entrypoint-initdb.d networks: - guac-net guacamole: image: guacamole/guacamole:latest container_name: guacamole restart: unless-stopped depends_on: - guacd - postgres environment: GUACD_HOSTNAME: guacd POSTGRESQL_HOSTNAME: postgres POSTGRESQL_DATABASE: guacamole_db POSTGRESQL_USER: guacamole_user POSTGRESQL_PASSWORD: ${GUAC_DB_PASSWORD} ports: - '8080:8080' networks: - guac-net networks: guac-net: driver: bridge volumes: postgres-data: DCEOF " # --- Generate the database init script --- echo "[7/9] Generating database initialisation schema..." pct exec "${CT_ID}" -- bash -c ' mkdir -p /opt/guacamole/initdb docker pull guacamole/guacamole:latest -q docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgresql \ > /opt/guacamole/initdb/001-init.sql 2>/dev/null # Validate the schema was generated properly SCHEMA_SIZE=$(wc -c < /opt/guacamole/initdb/001-init.sql) if [ "${SCHEMA_SIZE}" -lt 1000 ]; then echo "ERROR: Schema generation failed (${SCHEMA_SIZE} bytes)" echo "Check that AppArmor fix was applied correctly" exit 1 fi echo "Schema generated successfully (${SCHEMA_SIZE} bytes)" ' # --- Start the stack --- echo "[8/9] Starting Guacamole stack..." pct exec "${CT_ID}" -- bash -c ' cd /opt/guacamole docker compose up -d echo "Waiting for services to initialise..." sleep 20 docker compose ps ' # --- Create systemd service for auto-start --- echo "[9/9] Creating systemd service for auto-start..." pct exec "${CT_ID}" -- bash -c ' cat > /etc/systemd/system/guacamole.service << "SVCEOF" [Unit] Description=Apache Guacamole Docker Stack Requires=docker.service After=docker.service [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/guacamole 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 guacamole.service ' CT_IP_CLEAN=$(echo "${CT_IP}" | cut -d'/' -f1) echo "" echo "============================================" echo " Apache Guacamole LXC setup complete!" echo "============================================" echo "" echo " Web UI: http://${CT_IP_CLEAN}:8080/guacamole/" echo " Default: guacadmin / guacadmin (CHANGE IMMEDIATELY)" echo " DB Pass: ${GUAC_DB_PASSWORD}" echo "" echo " MIGRATION STEPS:" echo " 1. Login at http://${CT_IP_CLEAN}:8080/guacamole/" echo " 2. Change admin password immediately" echo " 3. Re-create RDP connections (Settings > Connections)" echo " 4. Set up NPM proxy (10.0.0.226):" echo " guac.hideawaygaming.com.au -> http://${CT_IP_CLEAN}:8080" echo " Enable WebSocket support (critical for RDP)" echo " 5. Test all RDP connections" echo " 6. Stop HA Guacamole add-on" echo "" echo " Docker management:" echo " pct exec ${CT_ID} -- docker compose -f /opt/guacamole/docker-compose.yml logs -f" echo " pct exec ${CT_ID} -- docker compose -f /opt/guacamole/docker-compose.yml restart" echo "============================================"