Original planning folder (no git repo) for the server diagnostics system that runs on CT 300. Live deployment is on claude-runner; this preserves the Agent SDK reference, PRD with Phase 2/3 roadmap, and N8N workflow designs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
11 KiB
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
-
Navigate to your Proxmox node
-
Click Create CT
-
Configure:
- CT ID: 300 (or next available)
- Hostname: claude-code
- Password: Set a secure password
- SSH Public Key: Add your key for access
-
Template:
- Template:
ubuntu-22.04-standardordebian-12-standard
- Template:
-
Resources:
- Disk: 16 GB (local-lvm)
- CPU: 2 cores
- Memory: 2048 MB
- Swap: 512 MB
-
Network:
- Bridge: vmbr0
- IPv4: DHCP or static (e.g., 10.10.0.50/24)
- Gateway: Your network gateway
-
DNS:
- Use host settings or configure manually
-
Click Finish and start the container
Via CLI (pct)
# 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
# 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
# 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
# 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
# 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:
- SSH into the LXC
- Run
claudeinteractively - 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
# 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
# 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
# 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
# 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
# Create skill directory
mkdir -p ~/.claude/skills/server-diagnostics
# Copy skill files (from development machine)
# Or clone from repo when available
Create config.yaml
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
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
# Pre-add Proxmox host to known hosts to avoid prompts
ssh-keyscan -H 10.10.0.11 >> ~/.ssh/known_hosts
10. Create Test Script
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
# 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
# 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:
# From Proxmox host
pct exec 300 -- claude -p "..." --output-format json
Troubleshooting
Claude Code Not Found
# Check npm global path
npm config get prefix
# Ensure it's in PATH
export PATH="$PATH:$(npm config get prefix)/bin"
SSH Permission Denied
# 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
# 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
# Check network configuration
ip addr
ping -c 3 google.com
# If no connectivity, check Proxmox network settings
Security Considerations
-
OAuth Credentials: Authentication tokens are stored in
~/.claude/. Ensure the LXC has restricted access and backups don't expose this directory. -
SSH Key Scope: The
claude_diagnostics_keyshould only be installed on servers that need automated diagnostics. -
Minimal Permissions: The SSH key on target servers could use
command=restrictions for additional security (Phase 2 enhancement). -
Network Isolation: Consider placing the Claude LXC on an internal-only network segment.
-
Session Security: Your Max subscription is tied to this LXC. Don't share access to the container.
Snapshot Before Production
# On Proxmox host, create snapshot
pct snapshot 300 before-production --description "Claude Code LXC fully configured"
Next Steps
- ✅ LXC created and configured
- ✅ Claude Code installed and authenticated
- ✅ SSH keys installed on target servers
- ⏳ Install server-diagnostics skill (when code ready)
- ⏳ Configure N8N workflow
- ⏳ Test end-to-end pipeline