Proxmox Firewall IP Sets and Security Groups Explained

Learn to use Proxmox's built-in firewall IP sets and security groups to enforce consistent network policies across all your VMs and containers.

Proxmox Pulse Proxmox Pulse
9 min read
Glowing network firewall shields with clustered IP address groups connected by security policy lines.

The Proxmox built-in firewall works at three levels — datacenter, node, and VM/container — and once you understand that hierarchy and start using IP sets and security groups, you'll spend far less time writing duplicate rules across a dozen VMs. By the end of this guide you'll have a functioning firewall policy that drops unwanted traffic, whitelists management access, and applies consistent rules to groups of machines through reusable security groups.

Key Takeaways

  • 3-tier hierarchy: Rules flow from datacenter → node → VM/CT; each level is enabled independently.
  • IP sets: Named lists of IPs and CIDRs — define once, reference with +name in any rule at any level.
  • Security groups: Reusable rule templates — apply the same policy to dozens of VMs without copy-pasting.
  • Firewall off by default: Enabling the datacenter firewall does NOT auto-enable it on nodes or VMs.
  • DROP gotcha: Setting policy_in: DROP without pre-allowing SSH (22) and the web UI (8006) will lock you out.

How the Proxmox Firewall Hierarchy Works

Proxmox VE 9.1 ships with a built-in stateful firewall implemented on top of nftables (Proxmox VE 8 and earlier used iptables). It operates at three distinct levels:

  1. Datacenter level (/etc/pve/firewall/cluster.fw) — global policies, IP sets, and security groups shared across all nodes.
  2. Node level (/etc/pve/nodes/<node>/host.fw) — rules that protect the Proxmox host OS itself.
  3. VM/CT level (/etc/pve/firewall/<vmid>.fw) — per-guest rules applied at the virtual NIC.

Both the host rules and the VM rules must allow a packet for it to pass. A request from the internet hits the node firewall first, then the VM's firewall. If either drops it, the packet is gone.

Enabling the Firewall at Each Level

Enable via the web UI: Datacenter → Firewall → Options → Firewall → Enable. Repeat under each Node → Firewall → Options, and each VM/CT → Firewall → Options.

Via the CLI:

# Enable datacenter-level firewall
pvesh set /cluster/firewall/options --enable 1

# Enable node-level firewall
pvesh set /nodes/pve1/firewall/options --enable 1

# Enable firewall on VM 100
pvesh set /nodes/pve1/qemu/100/firewall/options --enable 1

# Enable firewall on LXC container 200
pvesh set /nodes/pve1/lxc/200/firewall/options --enable 1

Before enabling the host firewall, add allow rules for SSH and the Proxmox web UI. Do it before you flip the switch — not after:

# Allow SSH to the host
pvesh create /nodes/pve1/firewall/rules \
  --action ACCEPT \
  --type in \
  --proto tcp \
  --dport 22 \
  --comment "SSH — tighten source to +management after initial setup"

# Allow Proxmox web UI
pvesh create /nodes/pve1/firewall/rules \
  --action ACCEPT \
  --type in \
  --proto tcp \
  --dport 8006 \
  --comment "Proxmox web UI"

Creating IP Sets for Trusted Network Groups

IP sets are named collections of IPs and CIDR ranges. Define a set once at the datacenter level; reference it in any rule at any level with +set_name. When your IP scheme changes, update the set in one place — every rule that references it updates automatically.

Define an IP Set via the CLI

# Create the IP set
pvesh create /cluster/firewall/ipset \
  --name management \
  --comment "Admin workstations and jump hosts"

# Add entries
pvesh create /cluster/firewall/ipset/management \
  --cidr 192.168.10.5/32 \
  --comment "Admin laptop"

pvesh create /cluster/firewall/ipset/management \
  --cidr 10.0.0.0/8 \
  --comment "Internal RFC1918 space"

The resulting section in /etc/pve/firewall/cluster.fw:

[IPSET management]
192.168.10.5/32  # Admin laptop
10.0.0.0/8       # Internal RFC1918 space
IP Set Contents Primary Use
management Admin workstations, jump hosts SSH and web UI allow rules
cluster_nodes All Proxmox node IPs Corosync and live migration
backup_servers Proxmox Backup Server IPs PBS agent and API traffic
monitoring Prometheus and Grafana nodes Metrics scrape port access
trusted_lans Internal RFC1918 subnets East-west VM traffic

Reference an IP set with the + prefix in any rule:

pvesh create /nodes/pve1/firewall/rules \
  --action ACCEPT \
  --type in \
  --proto tcp \
  --source '+management' \
  --dport 22 \
  --comment "SSH from admin IPs only"

Building and Applying Security Groups

Security groups are named rule templates. Write the rules once at the datacenter level, then attach the group to any VM or container. Update the group and all attached guests get the change immediately — no per-VM editing required.

Create a Security Group

# Create the group
pvesh create /cluster/firewall/groups \
  --group webserver \
  --comment "Standard web server — HTTP and HTTPS inbound"

# Add inbound rules to the group
pvesh create /cluster/firewall/groups/webserver \
  --action ACCEPT \
  --type in \
  --proto tcp \
  --dport 80 \
  --comment "HTTP"

pvesh create /cluster/firewall/groups/webserver \
  --action ACCEPT \
  --type in \
  --proto tcp \
  --dport 443 \
  --comment "HTTPS"

The group lands in /etc/pve/firewall/cluster.fw:

[group webserver]
IN ACCEPT -p tcp --dport 80 -comment HTTP
IN ACCEPT -p tcp --dport 443 -comment HTTPS

Apply a Security Group to a VM

Via the web UI: VM → Firewall → Add → select type "Security Group" → choose webserver.

The resulting /etc/pve/firewall/105.fw:

[RULES]
GROUP webserver

[OPTIONS]
enable: 1

You can stack per-VM rules below the GROUP line for ports unique to that VM. The group provides the baseline; per-VM rules handle the exceptions.

A Production-Ready Datacenter Firewall Config

Here's what a realistic cluster.fw looks like after combining IP sets and security groups:

[OPTIONS]
enable: 1
policy_in: DROP
policy_out: ACCEPT
log_ratelimit: enable=1,burst=5,rate=1/second

[IPSET management]
192.168.1.10/32  # ops workstation
10.10.10.0/24    # management VLAN

[IPSET cluster_nodes]
10.10.0.1/32     # pve1
10.10.0.2/32     # pve2
10.10.0.3/32     # pve3

[IPSET backup_servers]
10.10.20.5/32    # PBS node

[group base-host]
IN ACCEPT -source +management -p tcp --dport 22 -comment SSH
IN ACCEPT -source +management -p tcp --dport 8006 -comment WebUI
IN ACCEPT -source +cluster_nodes -p udp --dport 5404:5405 -comment Corosync
IN ACCEPT -source +cluster_nodes -p tcp --dport 60000:60050 -comment Migration
IN ACCEPT -p icmp -comment Ping

[group webserver]
IN ACCEPT -p tcp --dport 80 -comment HTTP
IN ACCEPT -p tcp --dport 443 -comment HTTPS

[group database]
IN ACCEPT -source +trusted_lans -p tcp --dport 5432 -comment PostgreSQL
IN ACCEPT -source +backup_servers -p tcp --dport 9090 -comment node-exporter

Apply base-host to every node's host firewall. Apply webserver or database to the appropriate VMs.

Critical Traffic to Allow Before Setting DROP Policy

Setting policy_in: DROP on nodes without these rules will break your cluster silently — no warnings, just stopped services:

Corosync — UDP 5404-5405 between all cluster nodes. Without this, quorum fails and VMs lose access to shared cluster resources.

Live migration — TCP 60000-60050. Missing this causes migrations to stall at 0% with no error message. A typical 10 GB RAM VM migrates in 30-60 seconds on a Gigabit link — if it hangs indefinitely, migration port access is the first thing to check.

VNC console — TCP 5900-5999. Without this, the noVNC console in the web UI connects but immediately drops the session.

SPICE — TCP 3128 (SPICE proxy). Only needed if you use SPICE display, but worth including in base-host preemptively.

All four belong in the base-host security group applied to every node. Adding a new node to your cluster is then a single GROUP reference rather than four individual rules.

Firewall Logging Without Flooding Syslog

Enabling logging with policy_in: DROP and no rate limit generates thousands of log lines per minute from internet scanners probing common ports. Proxmox VE 9's built-in rate limiter handles this:

# In [OPTIONS] block of cluster.fw
log_ratelimit: enable=1,burst=5,rate=1/second

Add explicit logging only on rules where an audit trail actually matters:

pvesh create /nodes/pve1/firewall/rules \
  --action ACCEPT \
  --type in \
  --proto tcp \
  --source '+management' \
  --dport 8006 \
  --log warning \
  --comment "Log web UI access"

Check live firewall events:

journalctl -f -u pve-firewall

Verify nftables rules are loaded after any config change:

nft list ruleset | grep -A 10 "pve-firewall"

Restart the service if rules are not showing up in the ruleset:

systemctl status pve-firewall
systemctl restart pve-firewall

If you are on Proxmox VE 8 or earlier with iptables, the same IP set and security group concepts apply — verification just uses iptables -L -n instead of nft list ruleset.

Pairing the Firewall with Dynamic Defenses

The built-in firewall handles static rules well but has no awareness of attack patterns or login attempts. For behavioral detection and automatic IP banning, pair it with CrowdSec on Proxmox: Cluster-Wide Brute-Force Defense. CrowdSec's nftables bouncer writes dynamic ban entries that coexist with Proxmox's nftables ruleset without conflict.

Before the firewall pays off fully, your VMs should be on isolated VLANs. Rules on a flat network where every VM can reach every other VM are far harder to reason about. Configuring VLANs on Proxmox with Linux Bridges covers that prerequisite — segmented VLANs with clear trust boundaries make security group policies meaningful rather than cosmetic.

If your goal is removing TCP 8006 from your public allow list entirely, Cloudflare Tunnel on Proxmox for Zero-Trust Remote Access eliminates the need to expose the web UI at all — the tunnel handles authentication before any packet reaches your host.

Conclusion

IP sets and security groups turn Proxmox's built-in firewall from a per-VM configuration chore into a maintainable policy system. Start by defining three or four IP sets (management IPs, cluster nodes, backup servers), build a base-host security group with essential cluster traffic rules, and apply it to every node before setting policy_in: DROP. From there, run pvesh get /cluster/resources --type vm to see which VMs still have the firewall disabled — that list is your next task.

Share
Proxmox Pulse

Written by

Proxmox Pulse

Sysadmin-driven guides for getting the most out of Proxmox VE in production and homelab environments.

Related Articles

View all →