Guides
fail2ban on Proxmox VE, Backup Server and Mail Gateway
The Proxmox web UI on port 8006 is a root login page. This guide installs fail2ban on a Proxmox VE 8 or 9 host, adds the filter file the packaged fail2ban doesn't ship, and covers SSH, PBS, PMG and guests.
To protect Proxmox VE with fail2ban, run apt install fail2ban python3-systemd on the host, create /etc/fail2ban/filter.d/proxmox.local (the Debian packages on PVE 8 and 9 don't ship the filter), and add a [proxmox] jail with port = https,http,8006, backend = systemd and journalmatch = _SYSTEMD_UNIT=pvedaemon.service. Enable [sshd] with backend = systemd too, then check fail2ban-client status proxmox.
The Proxmox shell runs as root, so the commands here omit sudo.
Why old Proxmox fail2ban guides fail#
Most tutorials were written for Proxmox VE 6 and 7 and break on current releases for three reasons:
- No rsyslog. Proxmox VE 8 and newer don't install rsyslog, so
/var/log/daemon.logand/var/log/auth.logdon't exist. A jail pointing at them stops fail2ban from starting withHave not found any log file for proxmox jail. Both the SSH and web UI jails must read the systemd journal. - No filter in the package. The
proxmoxfilter and the[proxmox]jail are new in fail2ban 1.1.1. Debian 12 (PVE 8) ships 1.0.2 and Debian 13 (PVE 9) ships 1.1.0; neither hasfilter.d/proxmox.confor a[proxmox]section injail.conf. Withfilter = proxmoxalone, fail2ban can't find the filter. - Even upstream assumes a log file. In 1.1.1 the stock
[proxmox]jail readslogpath = /var/log/daemon.log, and the filter has nojournalmatch.
| Product | Debian base | Packaged fail2ban | Default ban action | sshd jail out of the box | proxmox filter |
|---|---|---|---|---|---|
| Proxmox VE 9.x | 13 trixie | 1.1.0 | nftables | Works (journal) | No |
| Proxmox VE 8.x | 12 bookworm | 1.0.2 | iptables-multiport | Fails: no /var/log/auth.log |
No |
| Proxmox Backup Server 4.x / 3.x | 13 / 12 | 1.1.0 / 1.0.2 | as above | as above | Not applicable (own log) |
Install fail2ban on the Proxmox host#
fail2ban comes from the Debian repositories already configured on the host:
apt update
apt install fail2ban python3-systemd nftablespython3-systemd lets fail2ban read the journal. nftables provides the nft command used by the nftables ban action: fail2ban's package only recommends it, and Proxmox already ships iptables, which satisfies that recommendation, so nft can be missing. Check with command -v nft.
Create the proxmox filter#
If /etc/fail2ban/filter.d/proxmox.conf doesn't exist, create a local filter with the rule from the upstream 1.1.1 filter, plus a journal match:
[Definition]
_daemon = pvedaemon
failregex = ^\s*\S+ %(_daemon)s\[\d+\]: authentication failure; rhost=<ADDR> user=<F-USER>\S+</F-USER>
ignoreregex =
journalmatch = _SYSTEMD_UNIT=pvedaemon.servicefail2ban reads proxmox.local even when there's no proxmox.conf. When a future package adds the stock file, your .local is read after it and keeps the same rule, so there's no conflict and no dpkg prompt.
Every failed web UI login makes pvedaemon log a line like this:
Oct 25 18:36:10 pve1 pvedaemon[1234]: authentication failure; rhost=::ffff:203.0.113.42 user=root@pam msg=Authentication failureThe ::ffff: prefix is an IPv4 address mapped into IPv6. fail2ban's <ADDR> tag strips it and bans 203.0.113.42, which is what you want with an inet nftables table. The _daemon variable lets you reuse the filter for the Mail Gateway (below).
jail.local for SSH and the web UI (8006)#
[DEFAULT]
# admin network(s) and every cluster node: never ban these
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
backend = systemd
[proxmox]
enabled = true
port = https,http,8006
filter = proxmox
backend = systemd
journalmatch = _SYSTEMD_UNIT=pvedaemon.service
maxretry = 3The journalmatch line in the jail keeps it correct even if someone later relies on the stock 1.1.1 filter, which lacks one. A journal jail without a match reads every journal entry on the host, which wastes CPU. The backend guide explains how journal jails work.
On Proxmox VE 8, Debian 12's fail2ban still bans with iptables. Switch to nftables so bans live in their own table, clear of pve-firewall:
[DEFAULT]
banaction = nftables
banaction_allports = nftables[type=allports]On PVE 9, /etc/fail2ban/jail.d/defaults-debian.conf already sets nftables and a journal backend for sshd. Action details are in the nftables guide; hardening SSH itself is in the SSH guide.
Test and enable the Proxmox jail#
Log in to https://<host>:8006 once with a wrong password so there's something to match, then:
fail2ban-client -t
fail2ban-regex systemd-journal /etc/fail2ban/filter.d/proxmox.local
systemctl restart fail2ban
fail2ban-client status proxmoxfail2ban-regex takes the journal match from the filter file, and its summary should report at least one matched line. To test the firewall side without locking anyone out, ban a documentation address and look for it:
fail2ban-client set proxmox banip 192.0.2.15
nft list set inet f2b-table addr-set-proxmox
fail2ban-client set proxmox unbanip 192.0.2.15banip prints 1 when the address was added.
The Proxmox firewall and fail2ban#
pve-firewall (iptables) and the opt-in proxmox-firewall (nftables) manage their own rules. fail2ban's nftables action keeps its bans in a separate table, inet f2b-table, with an input chain at priority -1. A Proxmox firewall reload doesn't remove the bans, and a ban rejects the address on the jail's ports whatever the datacenter firewall allows.
Two precautions matter more on a hypervisor than anywhere else:
- Whitelist every cluster node and your management network in
ignoreip. A script failing logins on one node shouldn't ban another node's corosync or API traffic. - Keep out-of-band access. If you ban yourself on both SSH and 8006, the only way back is IPMI/iKVM or a physical console. From there,
fail2ban-client unban <IP>lifts the ban (unban an IP).
If you never log in to 8006 from the internet, a firewall rule that allows only your VPN is stronger than any jail, and fail2ban then only has to cover SSH.
Proxmox Backup Server (PBS)#
Proxmox Backup Server logs web and API authentication failures to its own file, /var/log/proxmox-backup/api/auth.log, so the proxmox filter doesn't match. A failure looks like this:
2026-10-25T18:36:10+02:00: authentication failure; rhost=[::ffff:203.0.113.42]:60267 user=fake@pam msg=user account disabled or expired.[Definition]
failregex = authentication failure; rhost=\[<ADDR>\]:\d+ user=<F-USER>\S+</F-USER>
ignoreregex =[proxmox-backup-server]
enabled = true
port = https,http,8007
filter = proxmox-backup-server
backend = auto
logpath = /var/log/proxmox-backup/api/auth.log
maxretry = 3This jail reads a file, so it sets backend = auto in case [DEFAULT] uses the journal. Install fail2ban on PBS the same way as on PVE, then test after a failed login on port 8007:
fail2ban-regex /var/log/proxmox-backup/api/auth.log /etc/fail2ban/filter.d/proxmox-backup-server.localProxmox Mail Gateway (PMG)#
PMG's web UI also listens on port 8006, but its API daemon is pmgdaemon. Confirm the log format on your host first:
journalctl -u pmgdaemon | grep 'authentication failure'If the lines match the PVE format with pmgdaemon[...], reuse the filter with a parameter:
[pmg]
enabled = true
port = https,8006
filter = proxmox[_daemon=pmgdaemon]
backend = systemd
journalmatch = _SYSTEMD_UNIT=pmgdaemon.service
maxretry = 3PMG is a mail server, so the SMTP side needs its own jails. The Postfix and Dovecot guide covers the postfix jails, which work with PMG's Postfix.
LXC containers and VMs#
- The host doesn't protect guests. Guest logs aren't in the host journal, and bridged traffic to a VM or container passes the host's forward path, not the input chain fail2ban hooks. A jail on the host protects only the host.
- Install fail2ban inside each guest that exposes a login, using the guide for its distribution (Debian, Ubuntu) and the app guide, such as Nginx or Nextcloud.
- LXC containers have their own network namespace, so fail2ban inside one bans traffic to that container only. Kernel modules can't be loaded from inside, so
nf_tablesmust already be loaded on the host; it usually is when the host uses nftables. - Reverse proxy in front of the guests? Then they log the proxy's IP. Fix the real client IP first (reverse proxy fix).
FAQ#
How do I install fail2ban on Proxmox?#
On the host shell, run apt install fail2ban python3-systemd nftables. Create /etc/fail2ban/filter.d/proxmox.local, then add [sshd] and [proxmox] jails to /etc/fail2ban/jail.local, both with backend = systemd; the proxmox jail also needs port = https,http,8006 and journalmatch = _SYSTEMD_UNIT=pvedaemon.service. Restart with systemctl restart fail2ban.
How do I protect the Proxmox web interface with fail2ban?#
Create a filter matching pvedaemon[...]: authentication failure; rhost=<ADDR> as /etc/fail2ban/filter.d/proxmox.local, because the Debian 12 and 13 packages don't include it. Add a [proxmox] jail with filter = proxmox, port = https,http,8006, backend = systemd and the pvedaemon journal match, then test with fail2ban-regex systemd-journal.
Why doesn't fail2ban work on Proxmox 8?#
Proxmox VE 8 (Debian 12) has no rsyslog, so /var/log/auth.log and /var/log/daemon.log don't exist and file-based jails can't start. Its fail2ban 1.0.2 also has no proxmox filter. Use backend = systemd for both jails, create the filter as a .local file, and set banaction = nftables.
Does fail2ban work on Proxmox 9?#
Yes. Proxmox VE 9 is based on Debian 13 with fail2ban 1.1.0, which already uses nftables and the journal for SSH. You still need to create the proxmox filter, since it was only added upstream in 1.1.1, and add the jail with journalmatch = _SYSTEMD_UNIT=pvedaemon.service.
Can fail2ban protect Proxmox Backup Server?#
Yes, with a custom filter. PBS logs failed logins to /var/log/proxmox-backup/api/auth.log as authentication failure; rhost=[address]:port user=.... Match that with rhost=\[<ADDR>\]:\d+ and ban ports https,http,8007. The Proxmox VE filter doesn't match PBS logs.
Does fail2ban on the Proxmox host protect VMs and LXC containers?#
No. Guest logins are logged inside the guest, and bridged guest traffic doesn't pass the host's input chain. Install fail2ban inside each VM or LXC container that exposes SSH or a web login. Inside an LXC container it bans traffic to that container only.
Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.