CPU-Only Ollama Cluster on Proxmox LXC: 3-Node Setup with Load Balancing

Run local LLMs across three Proxmox LXC containers with Ollama, expose them via Open WebUI's built-in load balancer, and avoid the Docker-in-LXC pitfalls that cost me two sessions to fix.

On this page
  1. Architecture
  2. Performance expectations
  3. Task 1: Create three Ubuntu 24.04 LXC containers
  4. Task 2: Install Ollama on each node
  5. Task 3: Deploy Open WebUI natively (not Docker)
  6. Routing policy: when to use local vs. cloud
  7. Service management reference

Running your own AI models at home — no cloud account, no per-token bills, no data leaving your network — is genuinely practical in 2026, even on commodity hardware with no GPU. The trade you’re making is speed: CPU inference produces roughly 2–12 tokens per second (a token is roughly three-quarters of a word), which feels like watching someone type quickly rather than the instant walls of text you get from cloud services. For many tasks, that’s a fine trade for free and private. This post covers deploying Ollama — the tool that makes running local models as easy as ollama pull — across three Proxmox LXC containers, wiring them to Open WebUI for a polished, load-balanced chat interface, and the specific gotchas that cost me real debugging time so they don’t cost you any.

The hardware running this cluster: four HP EliteDesk 705 G4 mini PCs with AMD Ryzen 5 Pro 2400G CPUs — around $120 each refurbished.


Architecture

Browser / App:8080Open WebUI (CT 103)10.0.0.69:8080 · OLLAMA_BASE_URLS round-robinCT 100 / pvelab0110.0.0.65:11434qwen2.5-coder:14b / llama3.2~2 tok/s · Ryzen 5 2400GCT 101 / pvelab0210.0.0.66:11434qwen2.5-coder:14b / llama3.2~2 tok/s · Ryzen 5 2400GCT 102 / pvelab0310.0.0.67:11434llama3.2~3 tok/s · Ryzen 5 2400Gpeira.dev

Performance expectations

Before diving in: CPU inference is slow compared to a GPU. Here’s what to expect on Ryzen 5 2400G hardware (consistent with community Ollama CPU benchmarks and MyAIHardware’s llama.cpp results for similar 8-thread AMD chips):

Model Size Tokens/sec Use case
llama3.2:3b ~2 GB 8–12 tok/s Quick queries, fast chat
qwen2.5-coder:14b ~9 GB 2–3 tok/s Code gen, complex reasoning

These speeds are per-node. The 14B model requires at least 14 GB RAM per container — nodes running it need 16 GB SO-DIMMs installed. Open WebUI’s round-robin distributes concurrent requests across all three nodes — parallel users each get a dedicated node.


Task 1: Create three Ubuntu 24.04 LXC containers

First: make these values your own

The container IDs (100103), node names (pvelab01pvelab04), and 10.0.0.x addresses throughout this guide are examples from my lab — substitute your own free IDs, hostnames, and LAN IPs everywhere they appear, including inside the OLLAMA_BASE_URLS line later on. If a value looks specific to one machine, it’s a placeholder to change — not a literal to copy.

1Download the Ubuntu 24.04 LXC template3 min

In the Proxmox web UI: select any node → local storage → CT Templates → Templates. Search for ubuntu-24.04 and click Download.

2Create CT 100 on pvelab015 min

Datacenter → pvelab01 → Create CT. Settings:

Field Value
VMID 100
Hostname ollama-node1
Template ubuntu-24.04
Disk 20 GB on local-lvm
Cores 4
RAM 14336 MB (14 GB)
Network DHCP → then set static to 10.0.0.65
Unprivileged Yes
Tip

Allocate 14 GB RAM per node. qwen2.5-coder:14b loads ~9 GB into memory; you need headroom for the OS and concurrent requests. Nodes with only 8 GB host the smaller llama3.2 model only.

3Repeat for CT 101 (pvelab02) and CT 102 (pvelab03)10 min

Create two more containers with the same spec. Assign:

  • CT 101: pvelab02, IP 10.0.0.66, RAM 6144 MB (8 GB node — llama3.2 only)
  • CT 102: pvelab03, IP 10.0.0.67, RAM 4096 MB (8 GB node — llama3.2 only)

Start all three containers.


Task 2: Install Ollama on each node

Run these steps inside each container. Access via pct exec <vmid> -- bash from the Proxmox host.

1Install Ollama via the official installer5 min
pct exec 100 -- bash (repeat for 101, 102)

curl -fsSL https://ollama.com/install.sh | sh
systemctl enable --now ollama
2Configure Ollama to listen on all interfaces3 min

By default, Ollama binds to localhost:11434. Open WebUI needs to reach it over the container network, so override the bind address:

Configure Ollama systemd override

mkdir -p /etc/systemd/system/ollama.service.d
cat > /etc/systemd/system/ollama.service.d/override.conf {'<<'} 'EOF'
[Service]
Environment="OLLAMA_HOST=0.0.0.0"
Environment="OLLAMA_NUM_THREADS=8"
EOF
systemctl daemon-reload && systemctl restart ollama
Note

OLLAMA_NUM_THREADS=8 pins Ollama to all 8 logical cores of the 2400G. Without this, Ollama may auto-detect fewer threads and leave performance on the table.

3Pull models on each node20–40 min depending on internet speed

On nodes with 14+ GB RAM (pvelab01):

CT 100 — pull both models

ollama pull llama3.2
ollama pull qwen2.5-coder:14b

On 8 GB nodes (pvelab02, pvelab03):

CT 101 / 102 — pull llama3.2 only

ollama pull llama3.2
4Verify all three nodes are responding2 min
Test from pvelab04 host

curl http://10.0.0.65:11434/api/tags
curl http://10.0.0.66:11434/api/tags
curl http://10.0.0.67:11434/api/tags

Each should return a JSON object listing installed models. If any fails, check systemctl status ollama inside that container.


Task 3: Deploy Open WebUI natively (not Docker)

Do not use Docker inside LXC

Docker inside an unprivileged LXC container requires nesting=1 and causes iptables/nftables rule conflicts. The Docker container also sets a FORWARD drop policy that blocks all traffic from your Ollama containers to the gateway. This is exactly what happened in my initial setup — it cost two sessions to diagnose and clean up. Use the native Python install instead.

1Create CT 103 for Open WebUI on pvelab035 min
Field Value
VMID 103
Hostname open-webui
IP 10.0.0.69
RAM 2048 MB
Cores 2
Unprivileged Yes
2Install Open WebUI in a Python venv10 min
Inside CT 103

apt-get update && apt-get install -y python3-venv python3-pip
useradd -m -s /bin/bash openwebui
su - openwebui -c "python3 -m venv /home/openwebui/venv"
su - openwebui -c "/home/openwebui/venv/bin/pip install open-webui"
3Create the systemd service with OLLAMA_BASE_URLS5 min
Create /etc/systemd/system/open-webui.service

cat > /etc/systemd/system/open-webui.service {'<<'} 'EOF'
[Unit]
Description=Open WebUI
After=network.target

[Service]
User=openwebui
WorkingDirectory=/home/openwebui
Environment="OLLAMA_BASE_URLS=http://10.0.0.65:11434;http://10.0.0.66:11434;http://10.0.0.67:11434"
Environment="DATA_DIR=/home/openwebui/data"
ExecStart=/home/openwebui/venv/bin/open-webui serve
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl enable --now open-webui

The semicolon-separated OLLAMA_BASE_URLS gives Open WebUI its round-robin load balancer. Requests are distributed across all three Ollama nodes automatically.

4Verify load balancing in the Open WebUI interface2 min

Open http://10.0.0.69:8080 in your browser. After creating an admin account, go to Admin → Settings → Connections. You should see all three Ollama endpoints listed with green status indicators.

http://10.0.0.69:8080/admin/settingsOllama API Connectionshttp://10.0.0.65:11434http://10.0.0.66:11434http://10.0.0.67:11434

Routing policy: when to use local vs. cloud

With three local nodes running, the question becomes: when to use local inference vs. Claude/GPT?

Model routing decisionUse local (Ollama)✓ Boilerplate & templates✓ Config file generation✓ Repetitive code tasks✓ Private/sensitive data✓ Batch offline processingUse cloud (Claude / GPT)✓ Multi-file architecture decisions✓ Complex debugging✓ High-stakes code review✓ Long multi-turn reasoning✓ Novel problem solving

The tl;dr: local inference is free and private; use it for everything that doesn’t require frontier-model reasoning quality.


Service management reference

Managing Ollama containers from any Proxmox host

# Check model list on all three nodes
for ct in 100 101 102; do
echo "=== CT $ct ==="; pct exec $ct -- ollama list
done

# Restart Ollama on a specific node
pct exec 100 -- systemctl restart ollama

# Watch inference logs in real time
pct exec 100 -- journalctl -u ollama -f

# Test a quick generation (expect ~2 tok/s)
curl http://10.0.0.65:11434/api/generate \
-d '{"model":"llama3.2","prompt":"Hello world","stream":false}'

Next in this series: wiring up Hermes Agent — a fully autonomous AI agent running on the cluster with Proxmox read access, web search, and automated weekly reports.


Related posts:


Recommended hardware for this setup:

Sources: Ollama, Ollama (GitHub), Open WebUI documentation.

This post contains Amazon affiliate links (tag: buildahomelab-20). I earn a small commission on qualifying purchases at no extra cost to you.

Comments

Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.