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>
479 lines
11 KiB
Markdown
479 lines
11 KiB
Markdown
# 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
|