Compare

fail2ban vs DenyHosts

DenyHosts was the go-to SSH brute-force blocker in the 2000s. Today it is SSH-only, rarely packaged and tied to TCP wrappers that most systems no longer honour. Here is the comparison, and how to move to fail2ban cleanly.

Updated 5 min readChecked against fail2ban 1.1.1

Use fail2ban, not DenyHosts. DenyHosts only protects SSH, its last tagged release dates from 2015, Debian stopped shipping it after Debian 7, and its classic blocking method — writing attackers into /etc/hosts.deny — only works if sshd was built with TCP wrappers support, which upstream OpenSSH removed in 2014 and RHEL 8+ and Fedora don't have at all. fail2ban covers SSH and 96 other filters, bans in the firewall and is actively maintained.

If you still run DenyHosts on an old server, it probably works in the narrow sense — but it is worth replacing, and the migration takes about ten minutes.

fail2ban vs DenyHosts at a glance#

Feature fail2ban DenyHosts
What it protects SSH plus mail, web, FTP, VPN, databases… (96 filters) SSH only
Detection source Log files, systemd journal SSH auth log file only (/var/log/auth.log or /var/log/secure)
Response Firewall (nftables, iptables, firewalld, ufw, pf), Cloudflare, mail, and optionally hosts.deny Writes /etc/hosts.deny; 3.x can also add iptables rules
Thresholds maxretry within findtime, per jail Separate limits for invalid users, valid users and root
Ban expiry bantime (10 min by default), incremental bans PURGE_DENY (empty by default = never purged)
Shared intel Can report to AbuseIPDB / blocklist.de Optional sync server (disabled by default)
IPv6 Yes Not documented
systemd journal Yes (backend = systemd) No
License GPL-2.0-or-later GPL-2.0
Maintenance Active, 1.1.1 (Aug 2026) Last tagged release v3.1 (Sep 2015); occasional commits since

How DenyHosts works#

DenyHosts is a Python script, usually run as a daemon, that reads the SSH log named in SECURE_LOG and counts failed logins per source address. Its stock denyhosts.conf uses three thresholds: 5 failures for users that don't exist (DENY_THRESHOLD_INVALID), 10 for real users (DENY_THRESHOLD_VALID) and 1 for root (DENY_THRESHOLD_ROOT). When an address crosses a threshold, DenyHosts appends a line to /etc/hosts.deny:

text
sshd: 203.0.113.42

The SSH daemon consults that file through TCP wrappers (libwrap) on each new connection and refuses listed hosts. By default PURGE_DENY is empty, so entries stay forever and the file grows indefinitely.

The GitHub-maintained 3.x line added an IPTABLES option that also inserts a firewall rule, and a BLOCKPORT option to limit it to port 22. It also kept an optional synchronization feature for sharing attackers between DenyHosts users, disabled by default.

Why DenyHosts is obsolete#

  • TCP wrappers are going away. OpenSSH removed libwrap support upstream in version 6.7 (2014). Debian and Ubuntu still patch it back into their openssh-server, so hosts.deny still works there, but Red Hat removed TCP wrappers entirely in RHEL 8 and Fedora dropped them too. On those systems, a DenyHosts install that only writes hosts.deny blocks nothing.
  • No journal support. Many current systems log SSH only to the systemd journal (Debian 12 doesn't install rsyslog by default), so there is no auth.log for DenyHosts to read.
  • SSH only. Attackers who move on to your mail server or web login are invisible to it.
  • Slow release cadence. The last tagged release is v3.1 from September 2015. The repository received a few maintenance commits in 2025, but there has been no stable release in a decade, and Debian removed the package after Debian 7 "wheezy".
  • Late blocking. hosts.deny is checked after the TCP connection is accepted, so attackers still reach sshd. A firewall ban drops packets before they get there.

How fail2ban does the same job#

The fail2ban equivalent of a DenyHosts setup is a single jail:

/etc/fail2ban/jail.local
[sshd]
enabled  = true
backend  = systemd
maxretry = 5
findtime = 10m
bantime  = 1d
bantime.increment = true

fail2ban reads the journal (or a log file), matches failures with the sshd filter — which also understands the sshd-session process name used since OpenSSH 9.8 — and bans through your distribution's default firewall action — nftables on current Debian and Ubuntu, firewalld on Fedora with fail2ban-firewalld. Bans expire after bantime and grow for repeat offenders. More options are on the SSH protection guide.

DenyHosts' "one strike for root" has no direct equivalent, but the better fix is in SSH itself: set PermitRootLogin no (or prohibit-password) in sshd_config so root password guesses can never succeed.

Migrate from DenyHosts to fail2ban#

  1. Stop DenyHosts so it no longer edits hosts.deny: sudo systemctl disable --now denyhosts (or remove its init script/cron entry on older systems).
  2. Save its block list if you want to keep the worst offenders: grep '^sshd:' /etc/hosts.deny > ~/denyhosts-blocked.txt.
  3. Clean /etc/hosts.deny: remove the lines DenyHosts added (they look like sshd: 203.0.113.42), keeping any entries you wrote by hand. Then remove the package and its work directory (commonly /var/lib/denyhosts).
  4. Install fail2ban with your package manager — see the install guides.
  5. Create /etc/fail2ban/jail.local with the [sshd] jail above. Put your own addresses in ignoreip (DenyHosts used allowed-hosts in its work directory for the same purpose). Why jail.local and not jail.conf is explained in jail.local explained.
  6. Start and check: sudo systemctl enable --now fail2ban then sudo fail2ban-client status sshd.
  7. Optionally re-ban the old list with a long ban: sudo fail2ban-client set sshd banip $(awk '{print $2}' ~/denyhosts-blocked.txt)

Re-importing old DenyHosts entries is usually not worth it — most of those addresses were transient bots years ago — but it doesn't hurt if the list is small.

Can you run DenyHosts and fail2ban together?#

Technically yes, since one writes hosts.deny and the other the firewall. In practice it only makes unbanning confusing: an address can be blocked by either tool and you have to check both. Pick fail2ban and remove DenyHosts.

FAQ#

Is DenyHosts still maintained?#

Barely. The GitHub project's last tagged release is v3.1 from September 2015, though a few maintenance commits landed in 2025. Debian dropped the package after Debian 7, and it protects only SSH. For a new server, use fail2ban or another maintained tool instead.

What is the difference between fail2ban and DenyHosts?#

DenyHosts watches only the SSH log and blocks attackers by writing them to /etc/hosts.deny, which relies on TCP wrappers. fail2ban watches logs or the systemd journal for many services and bans in the firewall, with expiring and incremental bans. fail2ban is more flexible and actively maintained.

Does /etc/hosts.deny still work for SSH?#

Only if your sshd is built with TCP wrappers. Upstream OpenSSH removed that support in 6.7; Debian and Ubuntu patch it back in, so it still works there. RHEL 8 and later, Rocky, AlmaLinux and Fedora don't support TCP wrappers, so hosts.deny has no effect on SSH on those systems.

How do I replace DenyHosts with fail2ban?#

Stop and remove DenyHosts, delete the sshd: IP lines it added to /etc/hosts.deny, install fail2ban, and enable an [sshd] jail in /etc/fail2ban/jail.local with backend = systemd on journal-only systems. Add your own IPs to ignoreip and check the jail with fail2ban-client status sshd.

Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.