{"id":169,"date":"2026-10-20T20:25:20","date_gmt":"2026-10-20T18:25:20","guid":{"rendered":"https:\/\/nodeguard.io\/blog\/?p=169"},"modified":"2026-10-20T20:25:24","modified_gmt":"2026-10-20T18:25:24","slug":"how-to-install-and-configure-fail2ban-on-ubuntu","status":"publish","type":"post","link":"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/","title":{"rendered":"How to Install and Configure Fail2Ban on Ubuntu: Stop Hackers in 6 Proven Steps"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Put an Ubuntu server on the public internet and the first failed login attempts usually arrive within minutes. Automated bots sweep whole IP ranges, guess passwords for root and common usernames, and hammer SSH around the clock. Most of them never get in, but they never stop trying either, and every attempt eats CPU time, fills your logs and keeps rattling a door you would rather keep quiet.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">This guide shows you how to install and configure Fail2Ban on Ubuntu so those attackers are banned automatically after a few failed attempts. You will set up Fail2Ban on Ubuntu 24.04 LTS or 26.04 LTS, protect SSH with a properly tuned Fail2Ban jail, add optional jails for Nginx, Apache and repeat offenders, link everything to UFW, and learn the handful of commands you need to check, ban and unban IP addresses. Each step explains why a setting exists, not just what to type, so you can adapt it to your own server with confidence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What you will have at the end of this guide:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fail2Ban installed, enabled and starting automatically on boot<\/li>\n\n\n\n<li>An SSH jail that bans attackers after three failed logins<\/li>\n\n\n\n<li>Longer bans for repeat offenders, with your own IP safely whitelisted<\/li>\n\n\n\n<li>A tested setup you can verify and manage from the command line<\/li>\n<\/ul>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_86 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">In This Guide<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#What_Is_Fail2Ban_and_How_Does_It_Work\" >What Is Fail2Ban and How Does It Work?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Why_Brute_Force_Attacks_Make_Fail2Ban_Essential\" >Why Brute Force Attacks Make Fail2Ban Essential<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Prerequisites_Before_You_Start\" >Prerequisites Before You Start<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#How_Fail2Ban_Configuration_Files_Are_Organized\" >How Fail2Ban Configuration Files Are Organized<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#How_to_Install_and_Configure_Fail2Ban_on_Ubuntu_Step_by_Step\" >How to Install and Configure Fail2Ban on Ubuntu: Step by Step<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Step_1_Update_Your_Server_and_Install_Fail2Ban_on_Ubuntu\" >Step 1: Update Your Server and Install Fail2Ban on Ubuntu<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Step_2_Back_Up_and_Review_the_Default_Fail2Ban_Configuration\" >Step 2: Back Up and Review the Default Fail2Ban Configuration<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Step_3_Create_jaillocal_and_Set_Global_Defaults\" >Step 3: Create jail.local and Set Global Defaults<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Step_4_Configure_the_SSH_Fail2Ban_Jail\" >Step 4: Configure the SSH Fail2Ban Jail<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Step_5_Test_Restart_and_Verify_Fail2Ban_on_Ubuntu\" >Step 5: Test, Restart and Verify Fail2Ban on Ubuntu<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Step_6_Manage_Bans_With_fail2ban-client\" >Step 6: Manage Bans With fail2ban-client<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#More_Fail2Ban_Jail_Examples_for_Web_Servers_and_Repeat_Offenders\" >More Fail2Ban Jail Examples for Web Servers and Repeat Offenders<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Using_Fail2Ban_With_UFW_on_Ubuntu\" >Using Fail2Ban With UFW on Ubuntu<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Troubleshooting_Fail2Ban_on_Ubuntu\" >Troubleshooting Fail2Ban on Ubuntu<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Fail2Ban_Best_Practices_Checklist\" >Fail2Ban Best Practices Checklist<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Frequently_Asked_Questions\" >Frequently Asked Questions<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Do_I_still_need_Fail2Ban_if_I_use_SSH_keys\" >Do I still need Fail2Ban if I use SSH keys?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Does_Fail2Ban_work_on_Ubuntu_2404_and_2604\" >Does Fail2Ban work on Ubuntu 24.04 and 26.04?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#How_do_I_unban_an_IP_address_in_Fail2Ban\" >How do I unban an IP address in Fail2Ban?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Will_Fail2Ban_slow_down_my_server\" >Will Fail2Ban slow down my server?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Should_I_edit_jailconf_or_jaillocal\" >Should I edit jail.conf or jail.local?<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/nodeguard.io\/blog\/how-to-install-and-configure-fail2ban-on-ubuntu\/#Final_Thoughts\" >Final Thoughts<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"What_Is_Fail2Ban_and_How_Does_It_Work\"><\/span>What Is Fail2Ban and How Does It Work?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/fail2ban\/fail2ban\" target=\"_blank\" rel=\"noopener\">Fail2Ban<\/a> is a free, open-source intrusion prevention tool written in Python. It watches your log files or the systemd journal for patterns that signal abuse, such as repeated failed SSH passwords, and then tells your firewall to block the offending IP address for a set period of time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It works in three layers that are worth understanding before you touch any Fail2Ban configuration:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Filters<\/strong> are regular expressions that recognise a bad event in a log line, such as &#8220;Failed password for root from 203.0.113.7&#8221;.<\/li>\n\n\n\n<li><strong>Actions<\/strong> define what happens when an IP crosses the line, typically adding a firewall rule through nftables, iptables or UFW.<\/li>\n\n\n\n<li><strong>Jails<\/strong> tie the two together. A Fail2Ban jail says which filter to watch, which log or journal to read, how many failures are allowed in what time window, and which action to apply.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The logic is simple. If one IP address triggers the filter <code>maxretry<\/code> times within <code>findtime<\/code>, it is banned for <code>bantime<\/code>. When the ban expires, the firewall rule is removed and the address gets a clean slate, unless it starts misbehaving again. The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fail2ban\" target=\"_blank\" rel=\"noopener\">Wikipedia overview of Fail2ban<\/a> covers the project&#8217;s history if you are curious where it came from.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"538\" src=\"https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-feature-1-1024x538.webp\" alt=\"How to install and configure Fail2Ban on Ubuntu diagram showing failed logins detected and banned at a red firewall\" class=\"wp-image-171\" srcset=\"https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-feature-1-1024x538.webp 1024w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-feature-1-300x158.webp 300w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-feature-1-768x403.webp 768w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-feature-1-900x473.webp 900w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-feature-1.webp 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Fail2Ban reads your logs, counts failures per IP, and bans repeat attackers at the firewall.<\/figcaption><\/figure>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">One important point: Fail2Ban does not replace a firewall or good authentication. It is a reactive layer that sits on top of them, cutting off noisy attackers before they can grind through thousands of guesses.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Why_Brute_Force_Attacks_Make_Fail2Ban_Essential\"><\/span>Why Brute Force Attacks Make Fail2Ban Essential<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/owasp.org\/www-community\/attacks\/Brute_force_attack\" target=\"_blank\" rel=\"noopener\">OWASP<\/a> describes brute force attacks as systematically trying many passwords or keys until one of them works. On a public server, these attempts are not personal; they are industrial. Botnets cycle through leaked password lists and default credentials against every reachable SSH port, web login form and mail service they can find.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Brute force attacks cause real damage even when they fail:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Resource drain.<\/strong> Thousands of SSH handshakes per hour cost CPU and memory, which matters on a small VPS.<\/li>\n\n\n\n<li><strong>Log noise.<\/strong> Real problems get buried under pages of failed logins, which slows down any incident response.<\/li>\n\n\n\n<li><strong>Eventual success.<\/strong> One weak password on one forgotten account is all an attacker needs. Banning after a few failures turns a million guesses into three.<\/li>\n\n\n\n<li><strong>Credential stuffing on apps.<\/strong> WordPress, phpMyAdmin and control panel logins get exactly the same treatment as SSH.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you have already disabled password logins and switched to <a href=\"https:\/\/nodeguard.io\/blog\/how-to-set-up-ssh-keys-on-ubuntu\">SSH keys<\/a>, the chance of a successful SSH guess is close to zero. Fail2Ban is still worth running, because it stops the resource drain, cleans up your logs and protects every other service that still accepts passwords. Running Fail2Ban on Ubuntu is one of the cheapest security wins available to any server owner.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Prerequisites_Before_You_Start\"><\/span>Prerequisites Before You Start<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>An Ubuntu server running 24.04 LTS or 26.04 LTS (22.04 also works with the same commands). A fresh <a href=\"https:\/\/nodeguard.io\/cloud-vps\">NodeGuard Cloud VPS<\/a> is a good starting point.<\/li>\n\n\n\n<li>A user with sudo privileges. If you are still logging in as root, follow our guide on <a href=\"https:\/\/nodeguard.io\/blog\/how-to-set-up-an-ubuntu-vps\">how to set up an Ubuntu VPS<\/a> first.<\/li>\n\n\n\n<li>Your own public IP address, so you can whitelist it. Run <code>curl -4 ifconfig.me<\/code> on your local machine to find it.<\/li>\n\n\n\n<li>Access to your provider&#8217;s web or VNC console, as a backup in case you ever lock yourself out.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"How_Fail2Ban_Configuration_Files_Are_Organized\"><\/span>How Fail2Ban Configuration Files Are Organized<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The single biggest mistake beginners make is editing the wrong file. Every Fail2Ban configuration file lives under <code>\/etc\/fail2ban\/<\/code>, and the package ships several files that can be replaced during upgrades:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>File or folder<\/th><th>Purpose<\/th><th>Should you edit it?<\/th><\/tr><\/thead><tbody><tr><td><code>fail2ban.conf<\/code><\/td><td>Daemon settings: log level, log target, database<\/td><td>Rarely<\/td><\/tr><tr><td><code>jail.conf<\/code><\/td><td>Default values and every bundled jail, mostly disabled<\/td><td><strong>Never<\/strong><\/td><\/tr><tr><td><code>jail.d\/defaults-debian.conf<\/code><\/td><td>Ubuntu&#8217;s own defaults, which enable the SSH jail<\/td><td>No<\/td><\/tr><tr><td><code>jail.local<\/code><\/td><td>Your personal overrides<\/td><td><strong>Yes<\/strong><\/td><\/tr><tr><td><code>filter.d\/<\/code><\/td><td>Regex filters for each supported service<\/td><td>Only to add custom filters<\/td><\/tr><tr><td><code>action.d\/<\/code><\/td><td>Firewall and notification actions<\/td><td>Rarely<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Fail2Ban reads these files in a fixed order: <code>jail.conf<\/code>, then anything in <code>jail.d\/*.conf<\/code>, then <code>jail.local<\/code>, and finally <code>jail.d\/*.local<\/code>. Later files override earlier ones, so a value in <code>jail.local<\/code> beats the same value in <code>jail.conf<\/code>. Keeping all of your changes in <code>jail.local<\/code> means an <code>apt upgrade<\/code> can never wipe out your Fail2Ban configuration, and you always know exactly where to look when something behaves unexpectedly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"How_to_Install_and_Configure_Fail2Ban_on_Ubuntu_Step_by_Step\"><\/span>How to Install and Configure Fail2Ban on Ubuntu: Step by Step<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">With the theory covered, here is the full process. The six steps take about 15 minutes, and each one builds on the last.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-6-steps-1024x576.webp\" alt=\"Six steps to set up Fail2Ban on Ubuntu, from installing the package to managing a Fail2Ban jail\" class=\"wp-image-172\" srcset=\"https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-6-steps-1024x576.webp 1024w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-6-steps-300x169.webp 300w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-6-steps-768x432.webp 768w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-6-steps-900x506.webp 900w, https:\/\/nodeguard.io\/blog\/wp-content\/uploads\/2026\/10\/fail2ban-ubuntu-6-steps.webp 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">The six steps in this guide, from install to day-to-day ban management.<\/figcaption><\/figure>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_1_Update_Your_Server_and_Install_Fail2Ban_on_Ubuntu\"><\/span>Step 1: Update Your Server and Install Fail2Ban on Ubuntu<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Fail2Ban is in Ubuntu&#8217;s official repositories, so there is nothing to download from third-party sites. You can confirm the version available for your release on <a href=\"https:\/\/packages.ubuntu.com\/search?keywords=fail2ban\" target=\"_blank\" rel=\"noopener\">Ubuntu Packages<\/a>. Refresh your package lists first, then install:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update &amp;&amp; sudo apt upgrade -y\nsudo apt install fail2ban -y\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Make sure the service is enabled at boot and running right now, then check its version and status:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl enable --now fail2ban\nfail2ban-client --version\nsudo systemctl status fail2ban\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see <code>active (running)<\/code> in green. Press <code>q<\/code> to leave the status screen. Ubuntu already enables a basic SSH jail through <code>defaults-debian.conf<\/code>, so your server is partly protected from this moment, but those defaults are generic. The next steps tune them properly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_2_Back_Up_and_Review_the_Default_Fail2Ban_Configuration\"><\/span>Step 2: Back Up and Review the Default Fail2Ban Configuration<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before changing anything, take a quick look at what ships with the package and keep a copy of the original for reference:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo cp \/etc\/fail2ban\/jail.conf \/root\/jail.conf.original\nsudo cat \/etc\/fail2ban\/jail.d\/defaults-debian.conf\nls \/etc\/fail2ban\/filter.d\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>filter.d<\/code> listing shows every service Fail2Ban understands out of the box: <code>sshd<\/code>, <code>nginx-http-auth<\/code>, <code>apache-auth<\/code>, <code>postfix<\/code>, <code>dovecot<\/code>, <code>recidive<\/code> and dozens more. You do not need to copy <code>jail.conf<\/code> to <code>jail.local<\/code>. Older tutorials recommend that, but it duplicates hundreds of lines and makes upgrades messy. A short <code>jail.local<\/code> that contains only your changes is cleaner and much easier to audit later.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_3_Create_jaillocal_and_Set_Global_Defaults\"><\/span>Step 3: Create jail.local and Set Global Defaults<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Open a new, empty file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo nano \/etc\/fail2ban\/jail.local\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Paste in the following <code>[DEFAULT]<\/code> section, replacing <code>203.0.113.10<\/code> with your own public IP address:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;DEFAULT]\n# Never ban localhost or your own IP (space-separated, CIDR allowed)\nignoreip = 127.0.0.1\/8 ::1 203.0.113.10\n\n# Ban for 1 hour after 5 failures within 10 minutes\nbantime  = 1h\nfindtime = 10m\nmaxretry = 5\n\n# Repeat offenders get longer bans each time, capped at one week\nbantime.increment = true\nbantime.maxtime   = 1w\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here is what each setting does:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>ignoreip<\/strong> is your safety net. Whitelisted addresses can never be banned, no matter how often you mistype a password. If your home IP changes often, add your office IP or VPN exit address as well.<\/li>\n\n\n\n<li><strong>bantime, findtime and maxretry<\/strong> form the core rule: five failures inside ten minutes earns a one-hour ban.<\/li>\n\n\n\n<li><strong>bantime.increment<\/strong> doubles the ban each time the same IP returns, so a persistent bot goes from one hour to two, four, eight and so on, up to the <code>bantime.maxtime<\/code> ceiling.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Values in <code>[DEFAULT]<\/code> apply to every Fail2Ban jail unless a jail overrides them, which is exactly what you will do for SSH next.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_4_Configure_the_SSH_Fail2Ban_Jail\"><\/span>Step 4: Configure the SSH Fail2Ban Jail<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">SSH is the most attacked service on any Linux server, so it deserves stricter rules than the defaults. Add this block to the bottom of the same <code>jail.local<\/code> file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;sshd]\nenabled  = true\nport     = ssh\nbackend  = systemd\nmaxretry = 3\nfindtime = 10m\nbantime  = 2h\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A few notes on these choices:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>backend = systemd<\/strong> tells Fail2Ban to read SSH events from the systemd journal instead of <code>\/var\/log\/auth.log<\/code>. It works whether or not rsyslog is installed, which avoids the classic &#8220;jail starts but never bans&#8221; problem on minimal images.<\/li>\n\n\n\n<li><strong>maxretry = 3<\/strong> is safe for key-based logins, because legitimate users with keys almost never fail three times in a row.<\/li>\n\n\n\n<li><strong>port = ssh<\/strong> means port 22. If you moved SSH to a custom port, put the number here instead, for example <code>port = 2222<\/code>, otherwise the bans will block the wrong port.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you want the filter to catch more probing behaviour, such as connections that disconnect before authenticating, add <code>mode = aggressive<\/code> to the block. Save the file with <code>Ctrl+O<\/code>, press <code>Enter<\/code>, then exit with <code>Ctrl+X<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_5_Test_Restart_and_Verify_Fail2Ban_on_Ubuntu\"><\/span>Step 5: Test, Restart and Verify Fail2Ban on Ubuntu<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Always test before you restart. A single typo in <code>jail.local<\/code> can stop the service from starting, which leaves you completely unprotected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo fail2ban-client -t\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If it reports that the configuration test is successful, restart the service and confirm the jail is active:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl restart fail2ban\nsudo fail2ban-client status\nsudo fail2ban-client status sshd\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The second command lists every running Fail2Ban jail. The third shows live statistics for SSH: currently failed, total failed, currently banned, and the list of banned IP addresses. On a public server, do not be surprised to see banned addresses within the first hour.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To prove the filter really matches your SSH log lines, run the built-in regex tester against the journal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo fail2ban-regex systemd-journal \/etc\/fail2ban\/filter.d\/sshd.conf\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Look for a non-zero number on the <code>matched<\/code> line of the results. For a live test, try logging in with a wrong password three times from a device that is not on your whitelist, such as a phone on mobile data. The fourth attempt should hang or be refused.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_6_Manage_Bans_With_fail2ban-client\"><\/span>Step 6: Manage Bans With fail2ban-client<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>fail2ban-client<\/code> is your control panel for day-to-day management. These are the commands you will actually use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Show all currently banned IPs across every jail\nsudo fail2ban-client banned\n\n# Unban a specific IP from the SSH jail\nsudo fail2ban-client set sshd unbanip 198.51.100.23\n\n# Manually ban an IP you have spotted in your logs\nsudo fail2ban-client set sshd banip 198.51.100.23\n\n# Reload after editing jail.local (no full restart needed)\nsudo fail2ban-client reload\n\n# Watch bans and unbans happen in real time\nsudo tail -f \/var\/log\/fail2ban.log\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The log file is worth checking every so often. Lines marked <code>Ban<\/code> and <code>Unban<\/code> show exactly what Fail2Ban is doing, and a sudden spike in bans is often the first sign that a new botnet has discovered your server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"More_Fail2Ban_Jail_Examples_for_Web_Servers_and_Repeat_Offenders\"><\/span>More Fail2Ban Jail Examples for Web Servers and Repeat Offenders<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">SSH is only one door. If your server runs a website, a few extra jails close the others. Add any of these to <code>jail.local<\/code>, then test and reload.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Nginx<\/strong> (password-protected areas and vulnerability scanners):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;nginx-http-auth]\nenabled = true\nbackend = auto<\/code><\/pre>\n\n\n<p>[nginx-botsearch]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">enabled = true backend = auto<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Apache<\/strong> (failed basic-auth logins):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;apache-auth]\nenabled = true\nbackend = auto\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Recidive<\/strong> (the repeat-offender jail):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;recidive]\nenabled  = true\nbackend  = auto\nbantime  = 1w\nfindtime = 1d\nmaxretry = 3\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>backend = auto<\/code> line matters here. Web servers write to log files rather than the journal, so these jails need to read files, and Ubuntu&#8217;s bundled paths already point at <code>\/var\/log\/nginx\/<\/code> and <code>\/var\/log\/apache2\/<\/code>. The recidive jail is especially powerful: it watches Fail2Ban&#8217;s own log, and any IP banned three times in one day by any Fail2Ban jail is blocked from every port for a full week.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For WordPress sites, note that Fail2Ban does not ship a WordPress filter. A plugin such as WP fail2ban writes failed logins to the system log so a jail can pick them up, which is far more effective against brute force attacks than limiting logins inside PHP alone.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Using_Fail2Ban_With_UFW_on_Ubuntu\"><\/span>Using Fail2Ban With UFW on Ubuntu<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">By default, Fail2Ban adds its own firewall rules directly. That works fine, but if you already <a href=\"https:\/\/nodeguard.io\/blog\/how-to-configure-ufw-firewall-on-ubuntu\">configure UFW firewall on Ubuntu<\/a>, you may prefer to see bans inside UFW instead. Fail2Ban ships a UFW action, so switching takes two lines in the <code>[DEFAULT]<\/code> section of <code>jail.local<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>banaction = ufw\nbanaction_allports = ufw\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reload Fail2Ban, and banned IPs will appear as <code>DENY<\/code> rules at the top of <code>sudo ufw status numbered<\/code>. The two tools complement each other perfectly: UFW decides which ports are open at all, and Fail2Ban dynamically blocks whoever abuses the ports you leave open. Make sure UFW is enabled before switching, otherwise the bans have nowhere to go.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Troubleshooting_Fail2Ban_on_Ubuntu\"><\/span>Troubleshooting Fail2Ban on Ubuntu<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most problems with Fail2Ban on Ubuntu fall into a few familiar patterns.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>You banned yourself.<\/strong> Log in through your provider&#8217;s web or VNC console, then run <code>sudo fail2ban-client set sshd unbanip YOUR_IP<\/code> and add that address to <code>ignoreip<\/code> so it cannot happen again.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The service will not start.<\/strong> Run <code>sudo fail2ban-client -t<\/code> to find the exact line with the error, and check <code>sudo journalctl -u fail2ban -n 50<\/code> for details. Misspelled option names and missing <code>=<\/code> signs are the usual culprits.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The jail runs but never bans anyone.<\/strong> This is almost always a backend mismatch. If the jail reads <code>\/var\/log\/auth.log<\/code> but rsyslog is not installed, that file never updates. Set <code>backend = systemd<\/code> for SSH, or install rsyslog and use <code>backend = auto<\/code> with <code>logpath = \/var\/log\/auth.log<\/code>. Confirm the fix with the <code>fail2ban-regex<\/code> test from Step 5.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Failed logins appear in the journal but are not matched.<\/strong> Newer OpenSSH releases, including the one in Ubuntu 26.04, log authentication failures from a process called <code>sshd-session<\/code> rather than <code>sshd<\/code>. Current Ubuntu packages of Fail2Ban account for this, but an older or manually installed build may not. Run <code>sudo apt install --only-upgrade fail2ban<\/code> and test again.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Your edits seem to be ignored.<\/strong> You probably changed <code>jail.conf<\/code> or forgot to reload. Move your settings into <code>jail.local<\/code> and run <code>sudo fail2ban-client reload<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Docker ports are still exposed.<\/strong> Docker inserts its own firewall chains ahead of the normal input rules, so standard bans never reach published container ports. Add <code>chain = DOCKER-USER<\/code> to the jails that protect those services.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Fail2Ban_Best_Practices_Checklist\"><\/span>Fail2Ban Best Practices Checklist<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Keep these habits and your setup will stay effective for years:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use SSH keys and disable password logins. Fail2Ban limits guessing; keys make guessing pointless.<\/li>\n\n\n\n<li>Always whitelist your own IP addresses in <code>ignoreip<\/code> before you tighten any rule.<\/li>\n\n\n\n<li>Keep every change in <code>jail.local<\/code>, never in <code>jail.conf<\/code>, and run <code>fail2ban-client -t<\/code> before each reload.<\/li>\n\n\n\n<li>Enable the recidive jail so persistent attackers earn week-long bans across every port.<\/li>\n\n\n\n<li>Avoid permanent bans (<code>bantime = -1<\/code>) unless you have a process to review them. Attacker IPs get recycled, and today&#8217;s botnet node could be a legitimate visitor next month.<\/li>\n\n\n\n<li>Combine Fail2Ban with a default-deny firewall so only the ports you actually need are reachable.<\/li>\n\n\n\n<li>Review <code>\/var\/log\/fail2ban.log<\/code> regularly and keep the package updated as part of your normal <code>apt upgrade<\/code> routine.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Frequently_Asked_Questions\"><\/span>Frequently Asked Questions<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Do_I_still_need_Fail2Ban_if_I_use_SSH_keys\"><\/span>Do I still need Fail2Ban if I use SSH keys?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. Keys stop passwords from being guessed, but bots will still connect thousands of times a day. Fail2Ban cuts that noise, saves resources and protects the other services that still rely on passwords, such as web logins and mail.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Does_Fail2Ban_work_on_Ubuntu_2404_and_2604\"><\/span>Does Fail2Ban work on Ubuntu 24.04 and 26.04?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. Both LTS releases include Fail2Ban in their official repositories, and every command in this guide works on both. Using <code>backend = systemd<\/code> for the SSH jail keeps it reliable on either version.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"How_do_I_unban_an_IP_address_in_Fail2Ban\"><\/span>How do I unban an IP address in Fail2Ban?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run <code>sudo fail2ban-client set JAILNAME unbanip IP_ADDRESS<\/code>, for example <code>sudo fail2ban-client set sshd unbanip 198.51.100.23<\/code>. To clear every ban at once, use <code>sudo fail2ban-client unban --all<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Will_Fail2Ban_slow_down_my_server\"><\/span>Will Fail2Ban slow down my server?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Fail2Ban uses very little memory and CPU, and it usually makes a server faster under attack, because banned IPs are dropped at the firewall before they ever reach SSH or your web server. Blocking brute force attacks early is far cheaper than processing them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Should_I_edit_jailconf_or_jaillocal\"><\/span>Should I edit jail.conf or jail.local?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Always <code>jail.local<\/code>. Package updates can overwrite <code>jail.conf<\/code>, while <code>jail.local<\/code> belongs to you and is read after the defaults, so its values always win.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Final_Thoughts\"><\/span>Final Thoughts<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You now know how to install and configure Fail2Ban on Ubuntu from scratch. You installed the package, learned how the Fail2Ban configuration files fit together, created a clean <code>jail.local<\/code>, tuned an SSH Fail2Ban jail, verified that it matches real log lines, and picked up the commands to manage bans day to day. Add the web and recidive jails, link everything to UFW, and your server will shrug off brute force attacks while you focus on running your projects.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Security works best in layers, and it works even better on infrastructure you control. A <a href=\"https:\/\/nodeguard.io\/cloud-vps\">NodeGuard Cloud VPS<\/a> gives you a clean Ubuntu 24.04 or 26.04 server with full root access, ready for Fail2Ban, UFW and your applications to run side by side. Spin one up, follow this guide, and lock the door before the bots arrive.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n","protected":false},"excerpt":{"rendered":"<p>Put an Ubuntu server on the public internet and the first failed login attempts usually arrive within minutes. Automated bots sweep whole\u2026<\/p>\n","protected":false},"author":2,"featured_media":170,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24,18],"tags":[88,36,85,87,35,72,7,41,84,34],"class_list":["post-169","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-security","category-linux","tag-brute-force-protection","tag-fail2ban","tag-firewall","tag-linux-security","tag-server-hardening","tag-ssh","tag-ubuntu","tag-ubuntu-server","tag-ufw","tag-vps-security"],"_links":{"self":[{"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/posts\/169","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/comments?post=169"}],"version-history":[{"count":1,"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/posts\/169\/revisions"}],"predecessor-version":[{"id":173,"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/posts\/169\/revisions\/173"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/media\/170"}],"wp:attachment":[{"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/media?parent=169"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/categories?post=169"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nodeguard.io\/blog\/wp-json\/wp\/v2\/tags?post=169"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}