Security

Get email or Telegram alerts when Fail2ban bans an IP

Make Fail2ban tell you when it bans someone. Use the built-in email action, add a custom action that posts to Telegram or a webhook, and keep the alerts from flooding your inbox.

IntermediateLinux server5 min read

Fail2ban works quietly. That is good most of the time, but you may want to know when it bans an address on a jail that rarely fires, such as an admin panel or a custom app. This guide shows two ways to get alerts: the built-in email action and a small custom action that sends a Telegram message or calls any webhook. It ends with ways to keep the alerts useful instead of noisy.

Before you start

  • A Linux VPS with Fail2ban running and a /etc/fail2ban/jail.local. See Configure Fail2ban jails.
  • Your own IP in ignoreip. Alerts are no comfort if the ban was you.
  • For email: a working way to send mail from the server. Many VPS networks restrict outbound port 25, so read Port 25 blocked on a VPS first. Sending through an external SMTP relay is the reliable choice.
  • For Telegram: a bot token from Telegram's BotFather and the chat ID you want messages sent to.
  • curl installed: sudo apt install curl or sudo dnf install curl.

Choose what to alert on

On a public server, the sshd jail can ban dozens of addresses a day. An alert for each one quickly becomes noise you ignore. A better plan:

  • Alert on rare, meaningful jails: an admin login, a custom app, the recidive jail for repeat offenders.
  • Leave busy jails like sshd silent, and check their counts now and then with fail2ban-client status sshd.

You set actions per jail, so this is easy to do.

Email alerts with action_mw

Fail2ban ships ready-made action shortcuts you can use in any jail:

  • %(action_)s bans only. This is the default.
  • %(action_mw)s bans and sends an email with a whois report on the address.
  • %(action_mwl)s does the same and includes the matching log lines.

These mail actions use the sendmail command, so you need a mail transfer agent (such as Postfix) or a sendmail-compatible relay tool installed and working. Test mail first:

Bash
echo "test" | sendmail -v you@example.com

Then set the addresses and pick the action in /etc/fail2ban/jail.local:

INI
[DEFAULT]
destemail = you@example.com
sender    = fail2ban@example.com
mta       = sendmail

[myapp-auth]
enabled = true
action  = %(action_mw)s

Setting action only inside one jail keeps other jails silent. Test and reload:

Bash
sudo fail2ban-client -t && sudo fail2ban-client reload

Note: The whois lookup needs the whois package. Install it with sudo apt install whois or sudo dnf install whois, or use %(action_)s plus a custom action instead.

Telegram or webhook alerts with a custom action

An action is a small file in /etc/fail2ban/action.d/ with commands that run on events. Fail2ban fills in tags such as <ip>, <name> (the jail) and <failures>.

Create the action:

Bash
sudo nano /etc/fail2ban/action.d/telegram.local
INI
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = curl -s -m 10 -X POST "https://api.telegram.org/bot<bot_token>/sendMessage" -d chat_id=<chat_id> --data-urlencode "text=[<name>] banned <ip> after <failures> failures on $(hostname)"
actionunban =

[Init]
bot_token = REPLACE_WITH_BOT_TOKEN
chat_id = REPLACE_WITH_CHAT_ID
  • Empty actionstart and actionstop mean you get no message each time Fail2ban restarts.
  • -m 10 stops curl from hanging the ban action if Telegram is slow.
  • Lock the file down, because it holds your bot token:
Bash
sudo chmod 600 /etc/fail2ban/action.d/telegram.local

For a generic webhook (Slack, Discord, Mattermost or your own endpoint), keep the same file shape and change actionban to post JSON to your URL. Check your chat service's documentation for the exact payload it expects.

Use the action in a jail

Keep the normal ban action and add yours on the next line, indented:

INI
[myapp-auth]
enabled = true
action  = %(action_)s
          telegram

The indent tells Fail2ban the second line belongs to the same setting. If you replace action with only telegram, you will get messages but nothing will actually be banned.

Reload:

Bash
sudo fail2ban-client -t && sudo fail2ban-client reload

Avoid alert floods

A few habits keep alerts readable:

  • Silence start and stop messages. The stock mail actions send a message when each jail starts and stops, so a reload can send one email per jail. To turn those off for all sendmail-based actions, create /etc/fail2ban/action.d/sendmail-common.local:
INI
  [Definition]
  actionstart =
  actionstop =
  • Alert on recidive instead of every jail. The recidive jail bans addresses that were banned repeatedly by other jails. One alert per persistent attacker is far more useful than one per scan.
INI
  [recidive]
  enabled  = true
  bantime  = 1w
  findtime = 1d
  action   = %(action_)s
             telegram
  • Use longer bans. Short bans mean the same address is banned, freed and banned again, sending an alert each time. Incremental bans (bantime.increment = true) help.

Check it works

Ban a test address by hand. Use a documentation address so nothing real is blocked:

Bash
sudo fail2ban-client set myapp-auth banip 198.51.100.20

You should get an email or a Telegram message within a few seconds. Then remove it:

Bash
sudo fail2ban-client set myapp-auth unbanip 198.51.100.20

If nothing arrives, look for action errors:

Bash
sudo grep -i "error\|failed" /var/log/fail2ban.log | tail -n 20

Troubleshooting

  • Bans work but no email arrives: the server cannot send mail. Test sendmail by hand, check your mail logs, and use an SMTP relay if port 25 is blocked. Check your spam folder too.
  • fail2ban.log shows the action failed with a curl error: the server cannot reach the API, or the token or chat ID is wrong. Run the curl command by hand with real values to see the response.
  • Telegram returns "chat not found": you have not started a conversation with your bot yet, or the chat ID is wrong. Send the bot a message first.
  • Dozens of emails after each reload: start and stop notifications are still on. Add the sendmail-common.local override above.
  • Alerts arrive but addresses are not banned: the jail's action only lists your notification. Put %(action_)s back as the first line.

Next steps

  • Build a filter worth alerting on: Write a custom Fail2ban filter.
  • See how many logins are being tried: See and reduce SSH brute-force attempts.
  • Get a message on every successful SSH login too: SSH login alerts.
  • Questions about your server? Ask MdmHost AI at any time.
Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.