Skip to main content
This comprehensive guide covers security best practices for Proxmox VE environments, from host hardening to container security.

Overview

Security should be implemented at multiple layers:

Host Security

Secure the Proxmox host itself

Container Security

Harden LXC containers and VMs

Network Security

Firewall and network isolation

Proxmox Host Hardening

Secure SSH Access

1

Disable Root Login

Edit /etc/ssh/sshd_config:
PermitRootLogin no
Create a sudo user:
adduser admin
usermod -aG sudo admin
2

Use SSH Keys

# On your local machine
ssh-keygen -t ed25519 -C "[email protected]"

# Copy to Proxmox host
ssh-copy-id admin@proxmox-host
Disable password authentication:
# In /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
3

Change SSH Port (Optional)

# In /etc/ssh/sshd_config
Port 2222
4

Restart SSH

systemctl restart sshd
Before disabling root login or password authentication, ensure you can login with your new user and SSH key!

Enable Two-Factor Authentication

1

Install Required Package

apt update
apt install libpam-google-authenticator
2

Configure for User

# Login as user
google-authenticator
Answer:
  • Time-based tokens: Y
  • Update .google_authenticator: Y
  • Disallow multiple uses: Y
  • Increase window: N
  • Enable rate-limiting: Y
3

Update PAM Configuration

Add to /etc/pam.d/sshd:
auth required pam_google_authenticator.so
4

Update SSH Configuration

In /etc/ssh/sshd_config:
ChallengeResponseAuthentication yes
5

Restart SSH

systemctl restart sshd

Configure Firewall

1

Enable Firewall at Datacenter Level

  1. DatacenterFirewallOptions
  2. Enable: Firewall
2

Create Security Group

DatacenterFirewallSecurity GroupCreateName: managementAdd rules:
IN ACCEPT -p tcp -dport 22 -source 192.168.1.0/24
IN ACCEPT -p tcp -dport 8006 -source 192.168.1.0/24
IN DROP
3

Apply to Host

NodeFirewallOptionsEnable firewall and apply security group

Automatic Security Updates

1

Install Unattended Upgrades

apt install unattended-upgrades apt-listchanges
2

Configure Updates

Edit /etc/apt/apt.conf.d/50unattended-upgrades:
Unattended-Upgrade::Origins-Pattern {
    "origin=Debian,codename=${distro_codename},label=Debian-Security";
    "origin=Proxmox";
};

Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
3

Enable Auto-Updates

Edit /etc/apt/apt.conf.d/20auto-upgrades:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
Set Automatic-Reboot "true" if you want automatic reboots for kernel updates, but schedule appropriately.

Fail2Ban Protection

1

Install Fail2Ban

apt install fail2ban
2

Configure SSH Protection

Create /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log

[proxmox]
enabled = true
port = https,http,8006
logpath = /var/log/daemon.log
maxretry = 3
bantime = 1h
3

Start Fail2Ban

systemctl enable fail2ban
systemctl start fail2ban
4

Monitor Bans

# Check status
fail2ban-client status

# Check specific jail
fail2ban-client status sshd

# Unban IP
fail2ban-client set sshd unbanip 192.168.1.100

Container Security

Unprivileged Containers

Unprivileged (Recommended)

Benefits:
  • Root in container ≠ root on host
  • Better security isolation
  • Limited escape vectors
  • Default for new scripts

Privileged

Use Only When:
  • Hardware access needed
  • Special capabilities required
  • Docker in LXC (with caution)
Create unprivileged container:
pct create 100 local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst \
  --unprivileged 1 \
  --hostname secure-ct
All community scripts default to unprivileged containers when possible.

AppArmor Profiles

AppArmor provides additional security for containers:
1

Check AppArmor Status

aa-status
2

Enable AppArmor for Container

pct set <CTID> -features nesting=1,keyctl=1
pct set <CTID> -protection 1
3

Apply Specific Profile

Edit /etc/pve/lxc/<CTID>.conf:
lxc.apparmor.profile: lxc-container-default-cgns

Container Hardening

Remove unnecessary Linux capabilities:
# Edit /etc/pve/lxc/<CTID>.conf
lxc.cap.drop: sys_module sys_rawio mknod
Common capabilities to drop:
  • sys_module - Load kernel modules
  • sys_rawio - Raw I/O operations
  • mknod - Create device nodes
  • sys_time - Set system time

Resource Limits

Prevent resource exhaustion:
# CPU limit (cores)
pct set <CTID> -cores 2

# CPU units (relative weight)
pct set <CTID> -cpuunits 1024

# Memory limit
pct set <CTID> -memory 2048

# Swap limit
pct set <CTID> -swap 512

# Disk I/O limit (MB/s)
pct set <CTID> -rootfs local-lvm:8,mbps_rd=100,mbps_wr=50

Application Security

Secure Web Applications

1

Use Reverse Proxy

Install Nginx Proxy Manager:
bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/nginxproxymanager.sh)"
Benefits:
  • SSL/TLS termination
  • Centralized access control
  • Hide backend services
2

Enable HTTPS

Use Let’s Encrypt for free SSL certificates:
  1. Configure Nginx Proxy Manager
  2. Add proxy host
  3. Request SSL certificate
  4. Force HTTPS redirect
3

Security Headers

Add security headers in Nginx:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;

Database Security

# Edit /etc/postgresql/15/main/pg_hba.conf
# Only allow local connections
local   all             all                                     peer
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256

# Change password encryption
# In postgresql.conf
password_encryption = scram-sha-256
# Run security script
mysql_secure_installation

# Disable remote root login
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');

# Create app-specific users
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
# Edit /etc/redis/redis.conf
bind 127.0.0.1 ::1
requirepass your_strong_password_here
maxmemory 256mb
maxmemory-policy allkeys-lru

# Disable dangerous commands
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG "CONFIG_a1b2c3d4"

Docker Security

Edit /etc/docker/daemon.json:
{
  "userns-remap": "default",
  "no-new-privileges": true,
  "icc": false,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Network Security

VLAN Segmentation

Isolate different types of services:

Trusted Network

VLAN 1 (Default)
  • Management interfaces
  • Proxmox host
  • Trusted services

IoT/Untrusted

VLAN 10
  • Smart home devices
  • Home Assistant
  • Limited internet access

DMZ Services

VLAN 20
  • Public-facing services
  • Web servers
  • Reverse proxies

Guest Network

VLAN 30
  • Guest Wi-Fi
  • Internet-only access
  • No local network access

Firewall Rules by Use Case

# Allow HTTP/HTTPS from anywhere
IN ACCEPT -p tcp -dport 80
IN ACCEPT -p tcp -dport 443

# Allow SSH from management network only
IN ACCEPT -p tcp -dport 22 -source 192.168.1.0/24

# Drop everything else
IN DROP

Intrusion Detection

Monitor for suspicious activity:
1

Install Suricata

apt install suricata
2

Update Rules

suricata-update
3

Configure Interface

Edit /etc/suricata/suricata.yaml:
af-packet:
  - interface: vmbr0
    cluster-id: 99
    cluster-type: cluster_flow
4

Start Service

systemctl enable suricata
systemctl start suricata

Backup Security

Encrypted Backups

1

Create Encrypted Storage

# Create encrypted directory
mkdir -p /backup/encrypted

# Mount encrypted filesystem
pvesm add dir encrypted-backup --path /backup/encrypted --content backup
2

Backup with Encryption

# Using gpg encryption
vzdump <CTID> --storage local --compress zstd | gpg --encrypt --recipient [email protected] > backup.vma.zst.gpg

Off-Site Backups

# Automated rsync backup
rsync -avz --delete /var/lib/vz/dump/ user@backup-server:/backups/proxmox/
Always test backup restoration regularly. An untested backup is not a backup.

Monitoring and Auditing

Log Monitoring

1

Centralized Logging

Install Loki for log aggregation:
bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/alpine-loki.sh)"
2

Configure Log Forwarding

Forward logs from containers to Loki
3

Set Up Alerts

Create alerts for:
  • Failed login attempts
  • High resource usage
  • Service failures
  • Unusual network traffic

Security Auditing

# Install Lynis
apt install lynis

# Run security audit
lynis audit system

# Review results
cat /var/log/lynis.log

Security Checklist

1

Host Security

  • SSH key authentication enabled
  • Root login disabled
  • Firewall configured
  • Fail2Ban installed
  • Automatic security updates enabled
  • Two-factor authentication configured
2

Container Security

  • Using unprivileged containers
  • AppArmor profiles applied
  • Resource limits set
  • Unnecessary capabilities dropped
  • Firewall rules configured
3

Network Security

  • VLANs implemented
  • Network segmentation configured
  • Firewall rules tested
  • Intrusion detection enabled
4

Application Security

  • HTTPS enabled
  • Strong passwords used
  • Databases hardened
  • Regular updates applied
  • Security headers configured
5

Backup & Recovery

  • Regular backups scheduled
  • Backups tested
  • Off-site backup configured
  • Encryption enabled
6

Monitoring

  • Log aggregation configured
  • Security alerts set up
  • Regular audits scheduled
  • Incident response plan documented

Additional Resources

Networking Guide

Advanced network configuration

Docker Security

Secure Docker deployment

Popular Apps

Secure application deployment
Security is an ongoing process. Regularly review and update your security measures.

Build docs developers (and LLMs) love