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.
curlinstalled:sudo apt install curlorsudo 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
recidivejail for repeat offenders. - Leave busy jails like
sshdsilent, and check their counts now and then withfail2ban-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_)sbans only. This is the default.%(action_mw)sbans and sends an email with a whois report on the address.%(action_mwl)sdoes 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:
echo "test" | sendmail -v you@example.comThen set the addresses and pick the action in /etc/fail2ban/jail.local:
[DEFAULT]
destemail = you@example.com
sender = fail2ban@example.com
mta = sendmail
[myapp-auth]
enabled = true
action = %(action_mw)sSetting action only inside one jail keeps other jails silent. Test and reload:
sudo fail2ban-client -t && sudo fail2ban-client reloadNote: 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:
sudo nano /etc/fail2ban/action.d/telegram.local[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
actionstartandactionstopmean you get no message each time Fail2ban restarts. -m 10stopscurlfrom hanging the ban action if Telegram is slow.- Lock the file down, because it holds your bot token:
sudo chmod 600 /etc/fail2ban/action.d/telegram.localFor 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:
[myapp-auth]
enabled = true
action = %(action_)s
telegramThe 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:
sudo fail2ban-client -t && sudo fail2ban-client reloadAvoid 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:
[Definition]
actionstart =
actionstop =- Alert on recidive instead of every jail. The
recidivejail bans addresses that were banned repeatedly by other jails. One alert per persistent attacker is far more useful than one per scan.
[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:
sudo fail2ban-client set myapp-auth banip 198.51.100.20You should get an email or a Telegram message within a few seconds. Then remove it:
sudo fail2ban-client set myapp-auth unbanip 198.51.100.20If nothing arrives, look for action errors:
sudo grep -i "error\|failed" /var/log/fail2ban.log | tail -n 20Troubleshooting
- Bans work but no email arrives: the server cannot send mail. Test
sendmailby hand, check your mail logs, and use an SMTP relay if port 25 is blocked. Check your spam folder too. fail2ban.logshows the action failed with a curl error: the server cannot reach the API, or the token or chat ID is wrong. Run thecurlcommand 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.localoverride above. - Alerts arrive but addresses are not banned: the jail's
actiononly lists your notification. Put%(action_)sback 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.
