# Claude Code LXC Setup Guide This guide walks through setting up a dedicated LXC container on Proxmox for running Claude Code in headless mode. ## Prerequisites - Proxmox VE host with available resources - Ubuntu/Debian LXC template - Anthropic API key (from console.anthropic.com) - SSH access to Proxmox host ## 1. Create LXC Container ### Via Proxmox Web UI 1. Navigate to your Proxmox node 2. Click **Create CT** 3. Configure: - **CT ID:** 300 (or next available) - **Hostname:** claude-code - **Password:** Set a secure password - **SSH Public Key:** Add your key for access 4. Template: - **Template:** `ubuntu-22.04-standard` or `debian-12-standard` 5. Resources: - **Disk:** 16 GB (local-lvm) - **CPU:** 2 cores - **Memory:** 2048 MB - **Swap:** 512 MB 6. Network: - **Bridge:** vmbr0 - **IPv4:** DHCP or static (e.g., 10.10.0.50/24) - **Gateway:** Your network gateway 7. DNS: - Use host settings or configure manually 8. Click **Finish** and start the container ### Via CLI (pct) ```bash # SSH to Proxmox host ssh root@proxmox-host # Create container pct create 300 local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst \ --hostname claude-code \ --memory 2048 \ --swap 512 \ --cores 2 \ --rootfs local-lvm:16 \ --net0 name=eth0,bridge=vmbr0,ip=dhcp \ --unprivileged 1 \ --features nesting=1 \ --start 1 # Set password pct exec 300 -- passwd root ``` ## 2. Initial Container Setup ```bash # Enter container pct enter 300 # Or SSH: ssh root@10.10.0.50 # Update system apt update && apt upgrade -y # Install essential packages apt install -y \ curl \ wget \ git \ openssh-client \ python3 \ python3-pip \ python3-venv \ ca-certificates \ gnupg \ jq \ vim # Install Node.js 20.x (required for Claude Code) curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt install -y nodejs # Verify installations node --version # Should be v20.x npm --version python3 --version ``` ## 3. Install Claude Code CLI ```bash # Install Claude Code globally npm install -g @anthropic-ai/claude-code # Verify installation claude --version ``` ## 4. Configure Authentication (Max Subscription) Using your Claude Max subscription is the recommended approach - no API key management needed, and you get the generous Max tier rate limits included in your $20/month subscription. ### Authenticate with Max Subscription ```bash # Run Claude Code interactively claude # You'll see a device code prompt: # ┌────────────────────────────────────────────────────┐ # │ To authenticate, visit: │ # │ https://console.anthropic.com/device │ # │ │ # │ Enter code: ABCD-1234 │ # └────────────────────────────────────────────────────┘ # 1. Open the URL in your browser (on any device) # 2. Log in with your Anthropic account (Max subscription) # 3. Enter the code shown in the terminal # 4. Claude Code will confirm authentication ``` Credentials are stored in `~/.claude/` and persist across sessions. ### Verify Authentication ```bash # Test headless mode claude -p "Say hello" --output-format json # Should return JSON with response like: # {"type":"result","result":"Hello! How can I help you today?","session_id":"..."} ``` ### Token Refresh OAuth tokens may expire after weeks/months. If headless mode starts failing with authentication errors: 1. SSH into the LXC 2. Run `claude` interactively 3. Re-authenticate via device code flow Consider adding a health check in N8N to detect auth failures and alert you. ## 5. Set Up SSH Keys for Server Access ```bash # Create .ssh directory mkdir -p ~/.ssh chmod 700 ~/.ssh # Generate dedicated key pair for diagnostics ssh-keygen -t ed25519 -f ~/.ssh/claude_diagnostics_key -N "" -C "claude-code-diagnostics" # View public key (copy this) cat ~/.ssh/claude_diagnostics_key.pub ``` ### Install Key on Target Servers ```bash # On Proxmox host (or other target servers) # Add the public key to authorized_keys echo "ssh-ed25519 AAAA... claude-code-diagnostics" >> ~/.ssh/authorized_keys # Or use ssh-copy-id from Claude Code LXC ssh-copy-id -i ~/.ssh/claude_diagnostics_key.pub root@10.10.0.11 ``` ### Test SSH Connection ```bash # From Claude Code LXC ssh -i ~/.ssh/claude_diagnostics_key root@10.10.0.11 "hostname && docker ps" # Should show hostname and Docker containers ``` ## 6. Install Python Dependencies ```bash # Install uv (fast Python package manager) curl -LsSf https://astral.sh/uv/install.sh | sh source ~/.bashrc # Create virtual environment for skills mkdir -p ~/.claude/skills cd ~/.claude/skills # Install skill dependencies uv venv source .venv/bin/activate uv pip install paramiko pyyaml ``` ## 7. Install Server Diagnostics Skill ```bash # Create skill directory mkdir -p ~/.claude/skills/server-diagnostics # Copy skill files (from development machine) # Or clone from repo when available ``` ### Create config.yaml ```bash cat > ~/.claude/skills/server-diagnostics/config.yaml << 'EOF' # Server Diagnostics Configuration servers: proxmox-host: hostname: 10.10.0.11 # Update with actual IP ssh_user: root ssh_key: ~/.ssh/claude_diagnostics_key description: "Main Proxmox host running Docker services" docker_containers: - name: tdarr critical: true restart_allowed: true - name: portainer critical: true restart_allowed: true - name: n8n critical: true restart_allowed: false - name: plex critical: true restart_allowed: true diagnostic_commands: disk_usage: "df -h" memory_usage: "free -h" cpu_usage: "top -bn1 | head -20" process_list: "ps aux --sort=-%mem | head -20" network_status: "ss -tuln" docker_ps: "docker ps -a --format 'table {{.Names}}\\t{{.Status}}\\t{{.Ports}}'" remediation_commands: docker_restart: "docker restart {container}" docker_logs: "docker logs --tail 500 {container}" denied_patterns: - "rm -rf" - "dd if=" - "mkfs" - "shutdown" - "reboot" - "> /dev/sd" EOF ``` ## 8. Configure Claude Code settings.json ```bash mkdir -p ~/.claude cat > ~/.claude/settings.json << 'EOF' { "permissions": { "allow": [ "Bash(ssh root@10.10.0.11 docker:*)", "Bash(ssh root@10.10.0.11 systemctl status:*)", "Bash(ssh root@10.10.0.11 journalctl:*)", "Bash(ssh root@10.10.0.11 df:*)", "Bash(ssh root@10.10.0.11 free:*)", "Bash(ssh root@10.10.0.11 ps:*)", "Bash(ssh root@10.10.0.11 top:*)", "Bash(ssh root@10.10.0.11 ss:*)", "Bash(python3 ~/.claude/skills/server-diagnostics/client.py:*)" ], "deny": [ "Bash(rm -rf:*)", "Bash(dd:*)", "Bash(mkfs:*)", "Bash(shutdown:*)", "Bash(reboot:*)", "Bash(*> /dev/sd*)" ] }, "model": "sonnet" } EOF ``` ## 9. Configure SSH for Known Hosts ```bash # Pre-add Proxmox host to known hosts to avoid prompts ssh-keyscan -H 10.10.0.11 >> ~/.ssh/known_hosts ``` ## 10. Create Test Script ```bash cat > ~/test-claude-headless.sh << 'EOF' #!/bin/bash # Test Claude Code headless mode echo "Testing Claude Code headless mode..." # Simple test result=$(claude -p "What is 2+2? Reply with just the number." --output-format json 2>&1) if echo "$result" | jq -e '.result' > /dev/null 2>&1; then echo "✅ Claude Code headless mode working" echo "Response: $(echo "$result" | jq -r '.result')" elif echo "$result" | grep -q "authenticate"; then echo "❌ Authentication required - run 'claude' interactively to log in" exit 1 else echo "❌ Claude Code headless mode failed" echo "Output: $result" exit 1 fi # Test SSH access echo "" echo "Testing SSH to Proxmox host..." if ssh -i ~/.ssh/claude_diagnostics_key -o ConnectTimeout=5 root@10.10.0.11 "echo 'SSH OK'" 2>/dev/null; then echo "✅ SSH connection working" else echo "❌ SSH connection failed" exit 1 fi # Test Docker access echo "" echo "Testing Docker access on Proxmox host..." containers=$(ssh -i ~/.ssh/claude_diagnostics_key root@10.10.0.11 "docker ps --format '{{.Names}}'" 2>/dev/null) if [ -n "$containers" ]; then echo "✅ Docker access working" echo "Containers found:" echo "$containers" | sed 's/^/ - /' else echo "⚠️ No containers found or Docker not accessible" fi echo "" echo "All tests completed!" EOF chmod +x ~/test-claude-headless.sh ``` ## 11. Run Verification Tests ```bash # Run the test script ~/test-claude-headless.sh ``` Expected output: ``` Testing Claude Code headless mode... ✅ Claude Code headless mode working Response: 4 Testing SSH to Proxmox host... ✅ SSH connection working Testing Docker access on Proxmox host... ✅ Docker access working Containers found: - tdarr - portainer - n8n - plex All tests completed! ``` ## 12. Configure N8N Access (Next Step) The N8N container needs to be able to invoke Claude Code on this LXC. Options: ### Option A: SSH from N8N to Claude LXC ```bash # On N8N container, generate key and copy to Claude LXC ssh-keygen -t ed25519 -f ~/.ssh/claude_lxc_key -N "" ssh-copy-id -i ~/.ssh/claude_lxc_key.pub root@10.10.0.50 # N8N Execute Command will use: # ssh -i ~/.ssh/claude_lxc_key root@10.10.0.50 "claude -p '...' --output-format json" ``` ### Option B: Local Execution (if N8N runs on same host) If N8N runs in a container on the Proxmox host, you can mount the Claude LXC filesystem or use `pct exec`: ```bash # From Proxmox host pct exec 300 -- claude -p "..." --output-format json ``` ## Troubleshooting ### Claude Code Not Found ```bash # Check npm global path npm config get prefix # Ensure it's in PATH export PATH="$PATH:$(npm config get prefix)/bin" ``` ### SSH Permission Denied ```bash # Check key permissions chmod 600 ~/.ssh/claude_diagnostics_key chmod 644 ~/.ssh/claude_diagnostics_key.pub # Check authorized_keys on target ssh root@10.10.0.11 "cat ~/.ssh/authorized_keys" ``` ### Authentication Expired ```bash # If headless mode returns auth errors, re-authenticate: claude # Follow the device code flow to log in again # Credentials will be refreshed in ~/.claude/ ``` ### Container Network Issues ```bash # Check network configuration ip addr ping -c 3 google.com # If no connectivity, check Proxmox network settings ``` ## Security Considerations 1. **OAuth Credentials**: Authentication tokens are stored in `~/.claude/`. Ensure the LXC has restricted access and backups don't expose this directory. 2. **SSH Key Scope**: The `claude_diagnostics_key` should only be installed on servers that need automated diagnostics. 3. **Minimal Permissions**: The SSH key on target servers could use `command=` restrictions for additional security (Phase 2 enhancement). 4. **Network Isolation**: Consider placing the Claude LXC on an internal-only network segment. 5. **Session Security**: Your Max subscription is tied to this LXC. Don't share access to the container. ## Snapshot Before Production ```bash # On Proxmox host, create snapshot pct snapshot 300 before-production --description "Claude Code LXC fully configured" ``` ## Next Steps 1. ✅ LXC created and configured 2. ✅ Claude Code installed and authenticated 3. ✅ SSH keys installed on target servers 4. ⏳ Install server-diagnostics skill (when code ready) 5. ⏳ Configure N8N workflow 6. ⏳ Test end-to-end pipeline