Your web VM is still responding, but you arrive at a moment when a rule that is too broad may open an unnecessary service or cut SSH.
The Proxmox firewall is already active, and the question becomes specific: should you write at the VM level, the node level, or the guest OS level?
For a VM or a container, start from the guest and its actual flows.
Read the options for net0, build the IN rules, decide on the fate of OUT, and then factor only the rules that deserve a group. Proxmox controls and application testing remain separate from the host firewall.
Set the target before writing the first rule
Identify the VMID, the virtual interface, the guest address, and the services that are actually exposed. A Debian VM serving HTTP and HTTPS does not have the same entries as an administration container accessible from a bastion.
- note the real
VMIDand thenet0interface; - list the incoming ports and the allowed sources;
- keep a console or administrative access to the host;
- check that the Datacenter/Node firewall has already been addressed.
Zones do not replace each other. Datacenter is mainly for reusable blocks, Node concerns the host, and VM/CT filters flows to or from the guest.
The Debian firewall remains a separate layer. The official Proxmox VE documentation describes this separation as well as the directions IN, OUT, and FORWARD.
If the node firewall is not yet activated or if you fear losing Web UI or SSH, stop before the VM rules and refer to the careful activation of the Proxmox firewall without losing access.
The locking of the host remains out of scope here.
Read the VM and its interface options
Before writing, review the configuration from the Proxmox node. Replace <VMID> with your VM’s numerical identifier :
qm config <VMID> --current
Then check in the VM configuration enable, policy_in, policy_out, log_level_in, log_level_out, and ipfilter.
Each interface also has its own firewall indicator. The general option of the VM is not sufficient if the net0 flag remains disabled.
[OPTIONS]
enable: 1
policy_in: DROP
policy_out: ACCEPT
log_level_in: nolog
log_level_out: nolog
ipfilter: 1
This block illustrates a restrictive entry and still permissive exit. This is not a file to copy as is : adapt the values to your VM, then verify the generated configuration.
ipfilter is mainly used to limit address spoofing on the interface, not to create an HTTP or SSH allowlist.
Allow only the truly necessary incoming flows
Start with the entries. For a web VM, SSH usually comes from a bastion or an administration network.
Ports 80 and 443 come from the reverse proxy or a public source defined by the architecture. An allowance rule must therefore specify the protocol, port, source, and interface when these elements are known.
[RULES]
IN ACCEPT -p tcp -dport 22 -source +admins -i net0
IN ACCEPT -p tcp -dport 80 -source +reverse_proxy -i net0
IN ACCEPT -p tcp -dport 443 -source +reverse_proxy -i net0
In this example, +admins and +reverse_proxy are separately defined IPSet.
The documentation address 192.0.2.0/24 can illustrate a network in an example or documentation, but it does not represent your actual network. Replace it with your verified sources, without copying an address found in a tutorial.
Place specific authorizations before a DROP or REJECT policy. A global deny added too early masks the useful rule.
It turns a simple oversight into an SSH cut. Review the configuration before hardening, then keep a host console available.
Decide on the OUT policy before making it strict
An allowed exit lets DNS, NTP, updates, APT repositories, monitoring, or external APIs work as long as other layers filter them.
A restrictive exit requires a precise inventory : the first blocked DNS packet is enough to make it seem like the VM or its bridge is down.
| VM Flow | Direction | Example | Risk under DROP |
|---|---|---|---|
| Admin SSH | IN | +admins to 22/tcp | Access denied if the source is not listed. |
| HTTP/HTTPS | IN | +reverse_proxy to 80/443 | The web service becomes unreachable. |
| DNS Resolution | OUT | UDP/TCP 53 to resolver | APT and name calls fail. |
| System Time | OUT | UDP 123 to NTP source | The clock drifts if no other mechanism takes over. |
| Repositories and APIs | OUT | TCP 80/443 to intended destinations | Updates and monitoring may stop. |
I prefer to leave policy_out: ACCEPT during the inventory, then switch to DROP with the documented exits one by one.
This choice protects the availability of the service during construction. It does not replace an outgoing policy if your environment really imposes a whitelist.
[OPTIONS]
policy_out: DROP
[RULES]
OUT ACCEPT -p udp -dport 53 -dest dns_interne
OUT ACCEPT -p tcp -dport 53 -dest dns_interne
OUT ACCEPT -p udp -dport 123 -dest ntp_interne
OUT ACCEPT -p tcp -dport 80 -dest depots
OUT ACCEPT -p tcp -dport 443 -dest depots
The names dns_interne, ntp_interne, and depots must point to your actual addresses or groups. The syntax for multiple ports and aliases should be reviewed with the installed version of Proxmox before application.
Factor reusable rules with a Security Group
A Security Group becomes useful when multiple VMs or containers share the same function.
Proxmox defines it at the cluster level, and then the configuration of each guest calls it in its [RULES] section. The group does not apply to any VM until you have inserted it into its rules.
# /etc/pve/firewall/cluster.fw
[group webserver]
IN ACCEPT -p tcp -dport 80
IN ACCEPT -p tcp -dport 443
# /etc/pve/firewall/<VMID>.fw
[RULES]
GROUP webserver
Keep in the group only the truly common rules. A VM-specific exception remains in /etc/pve/firewall/<VMID>.fw. This separation avoids modifying all guests to correct a single admin port.
Name sources with IPSet and aliases
IPSets group addresses or networks; aliases give a readable name to an address or a network.
Rules can use them as source or destination. This way you can read +admins or dns_interne without having to interpret a series of IP addresses each time.
# /etc/pve/firewall/cluster.fw
[ALIASES]
dns_interne 192.0.2.53
[IPSET admins]
192.0.2.10
[IPSET reverse_proxy]
192.0.2.20
192.0.2.21
The addresses 192.0.2.10, 192.0.2.20, and 192.0.2.21 are documentation placeholders. An IPSet is referenced with the + prefix, for example, -source +admins.
Without this prefix, Proxmox reads the name as an address or an alias, and the rule does not match the intent.
Do not confuse ipfilter-net0 with an application allowlist.
This IPSet attached to the interface helps prevent source address spoofing. It does not decide who can open port 443 of your application.
Read the order of rules before hardening the policy
A group rule or a local rule only takes effect if it is read at the expected place. Place precise matches before global deny and review the configuration after each change.
| Read Order | Example | Risk if placed too low |
|---|---|---|
| 1. Precise source and port | SSH from +admins to 22/tcp | The bastion is blocked by the policy. |
| 2. Reusable group | GROUP webserver | Web ports remain closed on this VM. |
| 3. Documented exits | DNS, NTP, repositories | The service starts then loses its dependencies. |
| 4. Default policy | DROP or REJECT | Any forgotten flow falls here. |
The official documentation describes the IN/OUT syntax, the options --source, --dest, --dport, and --iface, as well as the insertion of a Security Group.
Use the form that corresponds to your version, then check the compiled configuration instead of assuming the group is active.
Enable targeted logging during testing
Proxmox logging is disabled by default for filtered traffic. The options log_level_in and log_level_out enable the standard logs for the VM/CT, visible in Firewall → Log.
The added level to the log helps identify the packet : it does not automatically increase the number of logged packets.
[OPTIONS]
log_level_in: info
log_level_out: nolog
[RULES]
IN ACCEPT -p tcp -dport 22 -source +admins -i net0 -log info
Activate the level only during the testing of the flow that interests you, then revert to nolog if the volume becomes noisy.
A permanent global logging complicates reading actual denials and increases operational noise.
Note: an empty log does not prove that a service is working. You still need to check the rule, the application response, and the network path from the actual source.
Compile, test, and plan the rollback
Start by reading the firewall status on the node :
pve-firewall status
pve-firewall compile
pve-firewall status re-reads and compiles the rules : a syntax warning remains an issue to correct before the network test.
pve-firewall compile allows you to view the generated configuration.
The official pve-firewall documentation also describes pve-firewall simulate, which can help to simulate a source, destination, port, and protocol but does not prove kernel routing or application functionality.
The final check should come from the authorized source, then from a source that should be denied if you can use it without risk :
- SSH from the expected admin IP with
ssh -v; - HTTP or HTTPS from the reverse proxy with
curl -I https://<IP_VM>; - DNS, NTP, and APT from the VM if
policy_outis restrictive ; - reading the Proxmox log during the testing window, then reverting to
nolog.
Do not present these commands as a test already performed : they form your validation protocol.
An expected result is an authorized connection that responds, a denied flow that fails, and a trace consistent with the examined rule.
If a rule cuts off the VM, remove that rule or temporarily disable the firewall for that VM/CT.
Revert to policy_out: ACCEPT if DNS, NTP, or updates break.
Do not use pve-firewall stop as a normal rollback : this command removes Proxmox rules from the host and can leave the server unprotected.
Keep the limits visible
A VM without an IP address, a missing bridge, or a poorly carried VLAN cannot be repaired with a firewall rule.
If the rule seems correct but the guest does not have an IP, move on to network diagnostics of a Proxmox VM, from the bridge to the guest : restore the VM network.
For a host-side problem, the guide creating a Proxmox bridge without losing access to the server addresses the appropriate layer.
The backend nftables proxmox-firewall remains marked as tech preview in the PVE documentation 9.2.11.
The examples above remain focused on the stock pve-firewall backend. If your environment uses IPv6, also check NDP and IPv6 addresses instead of concluding from a test using IPv4 alone.