# Proxmox Optimization Guide — From Setup to Production

*GeniusTechLab Digital Guide*
*Version 1.0 — August 2026*

---

## Introduction

This guide takes you from a fresh Proxmox VE 8.x installation to a production-optimized virtualization platform. Every setting is battle-tested on real homelab and small-business deployments. Follow the sections in order — each builds on the previous.

---

## Phase 1: Post-Install Essentials

### 1.1 Repository Setup

```bash
# Remove enterprise repo (requires paid subscription)
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list

# Add free no-subscription repo
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
  > /etc/apt/sources.list.d/pve-no-subscription.list

# Update system
apt update && apt full-upgrade -y
apt install -y curl wget git htop vim net-tools dnsutils
pveam update  # Update LXC template list
```

### 1.2 Remove Subscription Banner

```bash
# Backup and edit the status check file
sed -i.bak "s/data.status !== 'Active'/false/" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
systemctl restart pveproxy
```

### 1.3 Network Configuration Best Practices

```bash
# Use a bridge for VM networking (if not already created)
# Edit /etc/network/interfaces:
#
# auto vmbr0
# iface vmbr0 inet static
#     address 192.168.1.10/24
#     gateway 192.168.1.1
#     bridge-ports eno1
#     bridge-stp off
#     bridge-fd 0
#     bridge-vlan-aware yes
#     bridge-vids 2-4094
#
# Apply: ifreload -a  (or reboot)
```

---

## Phase 2: Storage Optimization

### 2.1 ZFS Pool Creation (Recommended)

```bash
# For mirrored boot + data (2x NVMe for boot, 4x HDD for data)
# Boot pool (created during install, verify):
zpool status rpool
zpool set ashift=12 rpool

# Data pool — RAIDZ2 (survives 2 disk failures)
zpool create -o ashift=12 \
  -O compression=zstd-3 \
  -O atime=off \
  -O xattr=sa \
  -O recordsize=128K \
  -O mountpoint=/tank \
  tank raidz2 /dev/sda /dev/sdb /dev/sdc /dev/sdd

# Create purpose-specific datasets
zfs create tank/vms          # VM disk images
zfs create tank/containers   # LXC storage
zfs create tank/backups      # Proxmox Backup
zfs create tank/iso          # ISO images
zfs create tank/snippets     # Snippets/scripts
zfs create tank/media         # Media files (recordsize=1M)
zfs create tank/shared        # Shared data
```

### 2.2 ZFS Recordsize Optimization

| Dataset | Recordsize | Why |
|---------|-----------|-----|
| VM disks | 128K (default) | Matches VM block size |
| Databases | 16K | Small random I/O |
| Media files | 1M | Large sequential reads |
| Containers | 128K | General purpose |
| Backups | 1M | Large files |

```bash
# Set recordsize per dataset
zfs set recordsize=1M tank/media
zfs set recordsize=16K tank/vms/database
```

### 2.3 ZFS Compression Comparison

| Algorithm | Speed | Ratio | CPU Impact | Recommendation |
|-----------|-------|-------|------------|----------------|
| lz4 | Very fast | ~1.5-2x | Minimal | Default choice |
| zstd-1 | Fast | ~2-2.5x | Low | Good balance |
| zstd-3 | Medium | ~2.5-3x | Moderate | Recommended for storage |
| zstd-19 | Very slow | ~3-3.5x | High | Only for cold storage |
| gzip | Slow | ~2.5-3x | High | Don't use |

```bash
# Recommended: zstd-3 for most workloads
zfs set compression=zstd-3 tank
# For performance-sensitive (VM disks): lz4
zfs set compression=lz4 tank/vms
```

### 2.4 ARC (Adaptive Replacement Cache) Tuning

```bash
# Check current ARC size
arc_summary | head -20

# Set ARC max (default is up to 50% of RAM)
# For 64GB RAM, limit to 16GB for ARC (leave RAM for VMs)
echo "options zfs zfs_arc_max=17179869184" > /etc/modprobe.d/zfs.conf
# 17179869184 = 16GB in bytes

# Apply on next boot (ZFS module reload requires reboot)
update-initramfs -u
```

### 2.5 ZFS Scrub Schedule

```bash
# Monthly scrub for data pool
# Add to crontab:
echo "0 3 1 * * root zpool scrub tank" >> /etc/crontab

# Quarterly scrub for boot pool (less critical)
echo "0 3 1 1,4,7,10 * root zpool scrub rpool" >> /etc/crontab

# Check scrub status
zpool status tank
```

### 2.6 SSD/NVMe Optimization

```bash
# Enable TRIM for SSDs (important for performance)
zpool set autotrim=on tank

# For NVMe ZIL/SLOG device (if using)
zpool add tank log nvme0n1  # SLOG device
# Note: SLOG only helps with synchronous writes. Most VMs use async writes.
# Only add if you have sync write workloads (NFS, databases).

# Enable prefetching for spinning disks
echo "options zfs zfs_prefetch_disable=0" >> /etc/modprobe.d/zfs.conf  # HDD only
# For SSD/NVMe pools: keep prefetch disabled (default)
```

---

## Phase 3: VM & LXC Optimization

### 3.1 VM Disk Performance

| Setting | Value | Why |
|---------|-------|-----|
| Bus/Device | VirtIO SCSI single | Best performance |
| Discard | On | TRIM support for SSDs |
| Cache | Writeback (with safe storage) or None | Performance vs safety |
| I/O threads | 4+ | Parallel I/O |
| SSD emulation | On (if on SSD) | TRIM + NVMe optimizations |

```bash
# Optimize VM disk config (example VM 100)
qm set 100 -scsi0 tank/vms/100/vm-100-disk-0,discard=on,ssd=1,iothread=1
qm set 100 -scsihw virtio-scsi-single
```

### 3.2 CPU Optimization

| Setting | Value | Why |
|---------|-------|-----|
| CPU Type | host | Best performance (uses host CPU flags) |
| CPU sockets | 1 | Almost always use 1 socket |
| CPU cores | As needed | Match application requirements |
| NUMA | On (if multi-socket) | Better memory locality |
| CPU limit | Don't set | Use shares instead |
| CPU shares | 1024 (default) | Increase for priority VMs |

```bash
# Optimize CPU config
qm set 100 -cpu host -sockets 1 -cores 4 -numa 1
```

### 3.3 Memory Optimization

| Setting | Value | Why |
|---------|-------|-----|
| Memory | Allocate actual need | Overcommit carefully |
| Ballooning | Off (for performance) or On (for overcommit) | Trade-off |
| Minimum memory | Set if ballooning on | Prevent OOM |
| Hugepages | On for large VMs | Reduces TLB misses |

```bash
# Static memory (best performance)
qm set 100 -memory 8192 -balloon 0

# With ballooning (for overcommit)
qm set 100 -memory 8192 -balloon 2048  # Min 2GB, max 8GB
```

### 3.4 Network Optimization

| Setting | Value | Why |
|---------|-------|-----|
| Model | VirtIO (paravirtualized) | Best performance |
| Multiqueue | 4+ (for high throughput) | Parallel network I/O |
| Firewall | Off (use external firewall) | Less overhead |
| Disconnect on suspend | Off | Keep connections alive |

```bash
qm set 100 -net0 virtio,bridge=vmbr0,queues=4,firewall=0
```

### 3.5 LXC Optimization

LXC containers are more efficient than VMs — use them for Linux workloads when possible.

```bash
# Create optimized LXC container
pct create 200 debian-12-storage-template local:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst \
  -arch amd64 \
  -cores 2 \
  -memory 2048 \
  -swap 512 \
  -rootfs tank/containers/200 \
  -net0 name=eth0,bridge=vmbr0,ip=192.168.1.200/24,gw=192.168.1.1 \
  -unprivileged 1 \
  -features nesting=1 \
  -onboot 1 \
  -storage tank

# Nesting allows Docker inside LXC (if needed)
# Keysharing: -features nesting=1,keyctl=1
```

### 3.6 LXC vs VM Decision Matrix

| Use case | LXC | VM | Why |
|----------|-----|----|----|
| Docker host | ✅ | | LXC with nesting=1 |
| Linux web server | ✅ | | Lower overhead |
| Windows | | ✅ | Needs full virtualization |
| pfSense/OPNsense | | ✅ | BSD needs VM |
| TrueNAS | | ✅ | ZFS needs direct hardware |
| Proxmox in Proxmox | | ✅ | Full virtualization needed |
| Git server (Gitea) | ✅ | | Lightweight |
| Monitoring (Grafana) | ✅ | | Lightweight |
| Desktop/GUI | | ✅ | Better GPU support |

---

## Phase 4: Backup Strategy

### 4.1 Proxmox Backup Server (PBS)

```bash
# Install PBS on a separate machine or VM
# Download: https://www.proxmox.com/en/downloads
# Install Debian 12, then add PBS repo:
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
  > /etc/apt/sources.list.d/pbs-no-subscription.list
apt update && apt install proxmox-backup-server

# On Proxmox VE, add PBS as backup storage:
pvesm add pbs backup-pbs \
  --server pbs.local \
  --datastore main \
  --username root@pam \
  --password 'YOUR_PASSWORD' \
  --fingerprint YOUR_FINGERPRINT
```

### 4.2 Backup Schedule Template

| VM/CT | Schedule | Mode | Retention |
|-------|----------|------|-----------|
| Critical (DNS, firewall) | Daily 2AM | Snapshot | Keep 7 daily, 4 weekly, 3 monthly |
| Important (app servers) | Daily 3AM | Snapshot | Keep 7 daily, 4 weekly |
| Standard (media, dev) | Weekly Sun 4AM | Snapshot | Keep 4 weekly, 2 monthly |
| Archive (cold storage) | Monthly 1st 5AM | Full | Keep 12 monthly |

```bash
# Example backup job (via cron)
# Add to /etc/crontab on Proxmox host:
0 2 * * * root vzdump 100 --storage backup-pbs --mode snapshot --compress zstd
0 3 * * * root vzdump 101,102 --storage backup-pbs --mode snapshot --compress zstd
0 4 * * 0 root vzdump 200,201 --storage backup-pbs --mode snapshot --compress zstd
```

### 4.3 Backup Verification

```bash
# Verify backup integrity (run monthly)
# On PBS:
proxmox-backup-client verify --repository backup-pbs:main

# Test restore (run quarterly)
# Restore to a test VM and verify it boots:
qmrestore storage backup-pbs vm-100-disk-0.raw 999 --storage tank
qm start 999
# Check services, then destroy test VM
qm destroy 999 --purge
```

---

## Phase 5: Performance Monitoring

### 5.1 Built-in Monitoring

```bash
# Real-time performance
pveperf              # Quick benchmark
qm status 100 --verbose  # VM resource usage
pct status 200 --verbose  # Container resource usage

# ZFS performance
zpool iostat -v tank 2     # I/O stats every 2 seconds
zpool list -v              # Pool usage
arc_summary                # ARC hit rate (target > 90%)
zfs list -o space          # Dataset space usage
```

### 5.2 Prometheus + Grafana Monitoring

```bash
# Install Prometheus node exporter on Proxmox
apt install prometheus-node-exporter
systemctl enable --now prometheus-node-exporter

# Enable ZFS exporter
apt install prometheus-zfs-exporter  # or use zfs-exporter from source

# Key metrics to monitor:
# - node_zfs_arc_hits_total (ARC hit rate)
# - node_zfs_pool_size_bytes (pool usage)
# - node_disk_read_write_time_seconds (disk latency)
# - proxmox_vm_memory_used_bytes (VM memory)
# - proxmox_vm_cpu_usage (VM CPU)
```

### 5.3 Key Metrics & Alert Thresholds

| Metric | Warning | Critical | Action |
|--------|---------|----------|--------|
| ZFS pool usage | 70% | 85% | Add disks or clean up |
| ARC hit rate | <85% | <75% | Increase ARC size |
| Disk latency (read) | >20ms | >50ms | Check disk health |
| Disk latency (write) | >30ms | >100ms | Check ZFS txg sync |
| CPU usage (host) | 80% | 95% | Migrate VMs |
| Memory usage (host) | 85% | 95% | Reduce VM memory |
| Snapshot age | >24h | >48h | Check backup job |
| Scrub errors | >0 | >0 | Replace disk immediately |

---

## Phase 6: Security Hardening

### 6.1 Proxmox Host Hardening

```bash
# SSH key-only auth
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/#PermitRootLogin.*/PermitRootLogin without-password/' /etc/ssh/sshd_config
systemctl restart sshd

# Install fail2ban
apt install -y fail2ban
cat > /etc/fail2ban/jail.d/pve.conf << 'EOF'
[proxmox]
enabled = true
port = 8006
filter = proxmox
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
EOF

# Disable unused services
systemctl disable --now rpcbind 2>/dev/null
systemctl disable --now nfs-common 2>/dev/null

# Enable firewall (basic rules)
pve-firewall enable
# Default policy: drop incoming on datacenter level
# Allow: SSH(22), WebUI(8006) from LAN only
```

### 6.2 2FA Setup

```bash
# Install TOTP
apt install -y oathtool

# Enable 2FA for root@pam
# Via Web UI: Datacenter -> Permissions -> Realms -> pam -> Two Factor
# Or via CLI:
pveum usermod root@pam -totp
# Scan QR code with authenticator app
```

### 6.3 Network Security

```bash
# Restrict web UI to LAN only
# Edit /etc/default/pveproxy:
echo "ALLOW_FROM=192.168.1.0/24" > /etc/default/pveproxy
echo "DENY_FROM=all" >> /etc/default/pveproxy
systemctl restart pveproxy

# VLAN-aware bridge (already configured in 1.3)
# Assign VLANs to VMs:
qm set 100 -net0 virtio,bridge=vmbr0,tag=20  # VLAN 20
```

---

## Phase 7: GPU Passthrough

### 7.1 Enable IOMMU

```bash
# For Intel:
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt/' /etc/default/grub

# For AMD:
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="amd_iommu=on iommu=pt/' /etc/default/grub

update-grub
```

### 7.2 Load VFIO Modules

```bash
cat > /etc/modules-load.d/vfio.conf << 'EOF'
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
EOF

# Find GPU PCI IDs
lspci -nn | grep -i 'vga\|3d\|nvidia'
# Example: 01:00.0 VGA [0300]: NVIDIA AD102 [10de:2684]
#          01:00.1 Audio [0403]: NVIDIA [10de:22a1]

# Bind to VFIO
echo "options vfio-pci ids=10de:2684,10de:22a1" > /etc/modprobe.d/vfio.conf

# Blacklist native drivers (if GPU not used by host)
echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf
echo "blacklist nvidia" >> /etc/modprobe.d/blacklist.conf

update-initramfs -u
reboot
```

### 7.3 Assign GPU to VM

```bash
# Via CLI:
qm set 100 -hostpci0 01:00,x=vga,pcie=on,rombar=1

# For multiple GPUs:
qm set 100 -hostpci0 01:00,pcie=on -hostpci1 02:00,pcie=on

# Verify in VM:
lspci | grep -i vga
```

---

## Phase 8: Advanced Topics

### 8.1 High Availability (HA) — Multi-Node

```bash
# Add node to cluster (on existing node):
pvecm add NEW_NODE_IP

# Configure HA for a VM:
qm set 100 -onboot 1  # Start on boot
# Via Web UI: Datacenter -> HA -> Add
#   VM ID: 100
#   Max restart attempts: 3
#   Max relocate attempts: 1
```

### 8.2 Storage Replication (ZFS → ZFS)

```bash
# Replicate VM to another node (requires ZFS on both)
# Via Web UI: Datacenter -> Replication -> Add
# Or CLI:
pvesr create 100 --target node2 --schedule '*/15:00'  # Every 15 min
```

### 8.3 Cloud-Init Template

```bash
# Create a cloud-init template VM
# Download Debian cloud image:
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2

# Create VM:
qm create 9000 --name debian-template --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0
qm importdisk 9000 debian-12-genericcloud-amd64.qcow2 tank
qm set 9000 --scsihw virtio-scsi-single --scsi0 tank:vm-9000-disk-0,discard=on,ssd=1,iothread=1
qm set 9000 --ide2 tank:cloudinit
qm set 9000 --boot c --bootdisk scsi0
qm set 9000 --serial0 socket
qm template 9000

# Clone from template:
qm clone 9000 101 --name new-vm --full
qm set 101 --cicustom "user=vim\ncurl -fsSL https://get.docker.com | sh\n"
qm set 101 --ciuser debian --cipassword CHANGE_ME
```

---

## Quick Command Reference

| Task | Command |
|------|---------|
| List VMs | `qm list` |
| List containers | `pct list` |
| Start VM | `qm start 100` |
| Stop VM | `qm stop 100` |
| VM status | `qm status 100 --verbose` |
| VM config | `qm config 100` |
| Clone VM | `qm clone 100 101 --name clone --full` |
| Migrate VM | `qm migrate 100 node2 --online` |
| Snapshot | `qm snapshot 100 pre-update` |
| Rollback snapshot | `qm rollback 100 pre-update` |
| Backup VM | `vzdump 100 --storage tank --mode snapshot` |
| Restore VM | `qmrestore tank:dump/vm-100-disk-0.raw 101 --storage tank` |
| ZFS pool status | `zpool status tank` |
| ZFS I/O stats | `zpool iostat -v tank 2` |
| ARC stats | `arc_summary` |
| Check IOMMU groups | `for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d#*/iommu_groups/*}; n=${n%%/*}; printf 'IOMMU Group %s %s\n' $n; done` |

---

## Optimization Checklist

### Post-Install
- [ ] Remove subscription banner
- [ ] Set up no-subscription repository
- [ ] Update system fully
- [ ] Install essential packages
- [ ] Configure bridge network
- [ ] Set hostname and timezone

### Storage
- [ ] ZFS pool created with correct ashift
- [ ] Compression enabled (zstd-3)
- [ ] atime disabled
- [ ] Recordsize set per dataset
- [ ] ARC size tuned
- [ ] Scrub scheduled monthly
- [ ] Autotrim enabled (SSD)
- [ ] Snapshots scheduled

### VMs
- [ ] VirtIO SCSI single for all disks
- [ ] CPU type set to host
- [ ] Memory allocated correctly
- [ ] Network set to VirtIO with multiqueue
- [ ] Discard enabled (SSD)
- [ ] I/O threads enabled

### Backup
- [ ] PBS installed or backup storage configured
- [ ] Backup schedule set for all VMs
- [ ] Backup retention configured
- [ ] Test restore verified

### Security
- [ ] SSH key-only auth
- [ ] fail2ban installed
- [ ] 2FA enabled
- [ ] Web UI restricted to LAN
- [ ] Firewall enabled
- [ ] All passwords changed from defaults

### Monitoring
- [ ] Node exporter installed
- [ ] Grafana dashboard configured
- [ ] Alert thresholds set
- [ ] Disk health monitoring (smartctl)
- [ ] Email notifications configured

---

*© 2026 GeniusTechLab. This guide is for the purchaser's personal use. Do not redistribute. Contact us for team/site licensing.*