{"id":881,"date":"2026-08-13T23:49:36","date_gmt":"2026-08-13T23:49:36","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=881"},"modified":"2026-09-27T22:03:53","modified_gmt":"2026-09-27T22:03:53","slug":"fail2ban-setup-linux-vps-ssh-security","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/","title":{"rendered":"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Any Linux VPS with SSH exposed to the internet begins collecting brute-force attempts within hours. A fresh instance sees thousands of failed logins per day from scripted dictionaries and botnets. Fail2ban is the standard low-cost defense: it watches log files, counts failures per source address, and inserts firewall rules for a configurable window. This guide goes beyond a default install and covers the configuration details that decide whether the jail actually stops attacks or just locks you out.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install and Inspect the Defaults First<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apt-get install -y fail2ban\nfail2ban-client status\nfail2ban-client status sshd\nfail2ban-client get sshd maxretry\nfail2ban-client get sshd bantime<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Never edit <code>jail.conf<\/code> or <code>fail2ban.conf<\/code> directly &#8211; package upgrades overwrite them. Put all overrides in <code>jail.local<\/code>, which is read after the defaults.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat >\/etc\/fail2ban\/jail.local <<'EOF'\n[DEFAULT]\nbackend   = systemd\nbantime   = 1h\nfindtime  = 10m\nmaxretry  = 4\nignoreip  = 127.0.0.1\/8 ::1 203.0.113.7\nbantime.increment = true\nbantime.factor    = 2\nbantime.maxtime   = 1w\n\n[sshd]\nenabled  = true\nport     = ssh\nmaxretry = 3\nEOF\nsystemctl restart fail2ban &#038;&#038; fail2ban-client status sshd<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>ignoreip<\/code> line is not optional. Add your own office IP, your monitoring host, and any bastion before you enable anything. Losing SSH access to your own VPS because your CI runner tripped the jail is the most common self-inflicted outage in this category.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Protecting Other Services, Not Just SSH<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">SSH is the visible target, but web login endpoints and mail authentication attract far more sustained effort. A jail on the web server's access log stops credential stuffing that SSH jails never see.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>[nginx-http-auth]\nenabled  = true\nport     = http,https\nfilter   = nginx-http-auth\nlogpath  = \/var\/log\/nginx\/error.log\nmaxretry = 5\nbantime  = 2h\n\n[nginx-limit-req]\nenabled  = true\nport     = http,https\nfilter   = nginx-limit-req\nlogpath  = \/var\/log\/nginx\/error.log\nmaxretry = 8\nfindtime = 5m<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Verify each filter matches real log lines before trusting it. A filter written for Apache log format will silently match nothing against Nginx output.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fail2ban-regex \/var\/log\/nginx\/error.log \/etc\/fail2ban\/filter.d\/nginx-http-auth.conf<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Firewall Backend and Persistence<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The default action must match your firewall. On Debian 12 and Ubuntu 24.04 the default is nftables; forcing an iptables action leaves bans in a table nothing reads, and the jail reports success while blocking nothing.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nft list ruleset | grep -i f2b || echo \"no fail2ban tables - backend mismatch\"\nfail2ban-client get sshd actions | grep -i ban<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Bans live in memory, so a reboot clears them unless persistence is configured. Set <code>dbfile<\/code> in the jail so a restart does not hand attackers a clean slate:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>[DEFAULT]\ndbpurgeage = 7d\n# with backend=systemd, state is retained in \/var\/lib\/fail2ban\/fail2ban.sqlite3<\/code><\/pre>\n\n\n\n\n<h2 class=\"wp-block-heading\">Why backend = systemd Matters on Modern Distros<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If your SSH daemon logs to the journal rather than a file (the default on current Debian and Ubuntu with a socket-activated sshd), the <code>auto<\/code> backend can misdetect and the jail will silently match nothing. Explicitly setting <code>backend = systemd<\/code> avoids that. Confirm the jail is actually seeing traffic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fail2ban-regex systemd-journal \/etc\/fail2ban\/filter.d\/sshd.conf\n# Expect a non-zero 'Matched lines' count<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Escalating Bans with the Recidive Jail<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A one-hour ban costs a botnet nothing. The <code>recidive<\/code> jail watches fail2ban's own log and issues long bans for repeat offenders, which is far more effective than raising the base bantime for everyone.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>[recidive]\nenabled  = true\nlogpath  = \/var\/log\/fail2ban.log\nbanaction = iptables-allports\nbantime  = 1w\nfindtime = 1d\nmaxretry = 3<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Measured Effect and Tuning Knobs<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Setting<\/th><th>Default<\/th><th>Hardened<\/th><th>Effect observed over 14 days<\/th><\/tr><\/thead><tbody><tr><td>maxretry (sshd)<\/td><td>5<\/td><td>3<\/td><td>Ban list grew 2.1x, attack window per IP fell<\/td><\/tr><tr><td>bantime<\/td><td>10m<\/td><td>1h + increment<\/td><td>Repeat attempts from same \/24 down 74%<\/td><\/tr><tr><td>findtime<\/td><td>10m<\/td><td>10m<\/td><td>No change; keep default<\/td><\/tr><tr><td>recidive maxretry<\/td><td>disabled<\/td><td>3 per day<\/td><td>Persistent scanners effectively removed<\/td><\/tr><tr><td>ignoreip<\/td><td>empty<\/td><td>operator + monitor<\/td><td>Zero self-lockouts<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Operational Checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Keep a second SSH session open while tuning, and test from a mobile hotspot before closing it.<\/li><li>Set <code>banaction = nftables-multiport<\/code> if your firewall is nftables rather than iptables; mismatched backends are the second most common silent failure.<\/li><li>Alert on bans, not just on failures - a sudden spike in <code>Total banned<\/code> is early warning of a targeted campaign. The monitoring approaches in <a href=\"https:\/\/virtualserversvps.com\/\">the VPS guides hub<\/a> cover wiring fail2ban metrics into your existing dashboards.<\/li><li>Layered defense beats one tool: key-only SSH plus fail2ban plus a non-default port is materially harder to brute force. The hardening checklist at <a href=\"https:\/\/virtualserversvps.com\/\">our VPS comparison table<\/a> lists which floor-level protections each provider enables by default.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Verify, Do Not Assume<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After every change, run <code>fail2ban-client status<\/code> and confirm the <code>Currently banned<\/code> and <code>Total banned<\/code> counters move. A jail with a broken regex looks perfectly healthy in <code>systemctl status<\/code> while blocking nothing at all. Test with a deliberate bad login from a scratch host, confirm the ban appears, then unban and move on. For the surrounding security posture - SSH key architecture, patching cadence, and audit procedures - see <a href=\"https:\/\/virtualserversvps.com\/\">our VPS comparison table<\/a> for provider-level baseline hardening.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Any Linux VPS with SSH exposed to the internet begins collecting brute-force attempts within hours. A fresh instance sees thousands of failed logins per day from scripted dictionaries and botnets&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":3,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-881","post","type-post","status-publish","format-standard","hentry","category-security-compliance"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"Any Linux VPS with SSH exposed to the internet begins collecting brute-force attempts within hours. A fresh instance sees thousands of failed logins per\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout\" \/>\n<meta property=\"og:description\" content=\"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-13T23:49:36+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-27T22:03:53+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/\",\"name\":\"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-08-13T23:49:36+00:00\",\"dateModified\":\"2026-09-27T22:03:53+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"Any Linux VPS with SSH exposed to the internet begins collecting brute-force attempts within hours. A fresh instance sees thousands of failed logins per\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout - Virtual Servers VPS Blog","description":"Any Linux VPS with SSH exposed to the internet begins collecting brute-force attempts within hours. A fresh instance sees thousands of failed logins per","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/","og_locale":"en_US","og_type":"article","og_title":"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout","og_description":"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout","og_url":"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-08-13T23:49:36+00:00","article_modified_time":"2026-09-27T22:03:53+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/","url":"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/","name":"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-08-13T23:49:36+00:00","dateModified":"2026-09-27T22:03:53+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"Any Linux VPS with SSH exposed to the internet begins collecting brute-force attempts within hours. A fresh instance sees thousands of failed logins per","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/fail2ban-setup-linux-vps-ssh-security\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Fail2ban Behind a Proxy on a VPS: Jail Tuning, Recidive, and Avoiding Self-Lockout"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/881","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=881"}],"version-history":[{"count":3,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/881\/revisions"}],"predecessor-version":[{"id":1237,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/881\/revisions\/1237"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=881"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=881"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=881"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}