Guides
fail2ban for Minecraft servers
fail2ban has no stock Minecraft filter, and Minecraft logs very little that is useful for banning. This guide shows the one signal that works, a filter tested with fail2ban-regex, the jail for port 25565, and what fail2ban can't do for a game server.
fail2ban can protect a Java Edition Minecraft server, but only against one thing: clients that keep trying to join and get rejected during login. The usual case is a server scanner or griefer bot trying to join a whitelisted server. fail2ban 1.1.1 ships no Minecraft filter. You create one that matches the lost connection: You are not white-listed on this server! line in logs/latest.log, then add a jail that bans TCP port 25565. It doesn't stop DDoS floods, and it can't see attacks that never reach the log.
What Minecraft logs, and what it doesn't#
Minecraft writes everything to logs/latest.log in the server directory (the file is rotated to a dated .log.gz on every restart). Most player events name the player but not the address. A ban needs the address, so only a few lines are usable:
[18:36:12] [Server thread/INFO]: Disconnecting Steve (/203.0.113.42:51234): You are not white-listed on this server!
[18:36:12] [Server thread/INFO]: Steve (/203.0.113.42:51234) lost connection: You are not white-listed on this server!
[18:36:20] [Server thread/INFO]: Notch (/198.51.100.7:40122) lost connection: Failed to verify username!
[18:36:31] [Server thread/INFO]: Steve[/192.0.2.99:41000] logged in with entity id 123 at (0.5, 64.0, 0.5)
[18:36:40] [Server thread/INFO]: Steve lost connection: Disconnectedlost connection: You are not white-listed on this server!is a client that isn't on your whitelist. On a private server, every one of these is a stranger. This is the line to ban on.lost connection: Failed to verify username!is an online-mode server rejecting a client whose session Mojang couldn't confirm: a cracked client, a bot or a bad session token. A genuine player can hit it too, for example when the same account is signed in on two launchers.Disconnecting …often appears just before thelost connectionline for the same rejection. Match only one of the two, or every rejection counts twice.- Lines after login (
logged in,lost connection: Disconnected, chat, commands) don't need banning. Once a player is in, moderation is a job for/ban.
The address is written as /IP:port, with brackets around IPv6. Older server versions print a com.mojang.authlib.GameProfile@…[id=…,name=…] object where newer ones print the name. Paper and Spigot use a shorter prefix ([18:36:12 INFO]: …) but the same messages. Recent versions also have a log-ips setting in server.properties. It must stay true (the default), or the address is withheld and no filter can work.
Step 1: create the filter#
[Definition]
# Login-phase rejections only: these are the lines that carry the client address.
failregex = ^[^\]]*\](?: \[[^\]]+\])?: (?:\S+ )?\(/\[?<ADDR>\]?:\d+\) lost connection: (?:You are not white-listed on this server!|Failed to verify username!)\s*$
ignoreregex =
# latest.log only has the time of day; fail2ban assumes today's date.
datepattern = ^\[%%H:%%M:%%SHow it reads the line: datepattern takes the [18:36:12 timestamp. The regex then skips the rest of the prefix (] [Server thread/INFO]: on vanilla, INFO]: on Paper), an optional player name or profile, and captures the address between (/ and :port). Because it is anchored at both ends and the name can't contain spaces, text a client chooses can't move the match. If you don't want genuine players with a bad session to count, delete |Failed to verify username!.
Test it against your log before going live:
sudo fail2ban-regex /opt/minecraft/logs/latest.log /etc/fail2ban/filter.d/minecraft.localOn our test log the result was 5 matched. All five were lost connection lines: whitelist and session rejections, an old-style GameProfile line, and a Paper-format IPv6 line. The Disconnecting, logged in and ordinary disconnect lines were missed, as intended. We tested with fail2ban-regex from fail2ban 1.1.1.
Step 2: add the jail#
[minecraft]
enabled = true
filter = minecraft
logpath = /opt/minecraft/logs/latest.log
backend = auto
port = 25565
protocol = tcp
maxretry = 3
findtime = 10m
bantime = 1d- Set
logpathto your server's reallogs/latest.log. The fail2ban daemon runs as root, so it can read it wherever the server runs. backend = automatters on Ubuntu 24.04. Its package setsbackend = systemdfor all jails, and that setting ignoreslogpath.- If you changed
server-port, changeportto match. To block the address from everything on the host, including SSH and the query port, usebanaction = %(banaction_allports)sin place ofportandprotocol. - Add your own home IP and your friends' static addresses to
ignoreipif they ever join from a machine that isn't whitelisted yet.
Reload and check:
sudo fail2ban-client reload
sudo fail2ban-client status minecraftMinecraft in Docker (itzg/minecraft-server)#
With the popular itzg/minecraft-server image, the server directory is the /data volume, so the log is at <your volume>/logs/latest.log on the host. Point logpath there. The ban must also land in the chain Docker uses for published ports, or it won't affect the container. For iptables actions, add chain = DOCKER-USER to the jail. The full explanation and the nftables variant are in fail2ban with Docker.
Behind Velocity, BungeeCord or a hosting panel#
With a proxy in front, the backend servers see the proxy's address unless forwarding is set up, and the whitelist check may happen on the proxy instead. Run fail2ban on the machine that has the public port. Point the jail at the log that shows the real client address, and check that log's lines in the regex tester first. Put the proxy's own address in ignoreip. Banning it would kick every player. On shared game hosting with no root access you can't run fail2ban at all. Use the host's firewall or DDoS settings instead.
What fail2ban can't do for a Minecraft server#
- DDoS and connection floods. A flood that never completes a login writes nothing useful to
latest.log, and a large one saturates the link before any log line exists. That needs your provider's DDoS protection or a proxy-level rate limit. - Bedrock Edition. Bedrock Dedicated Server's connection messages give the player name and XUID, not the IP address, so there is nothing for a filter to match.
- Offline-mode servers. Login plugins such as AuthMe handle passwords there, and their failed-login messages often don't include the address. Use the plugin's own lockout settings, and only switch to fail2ban if its log line carries the IP.
- RCON. Keep
enable-rcon=false, or bind it to localhost and block port 25575 in your firewall. It shouldn't be reachable from the internet at all.
The most useful fail2ban jail on a game server is often the plain SSH jail: the host's SSH port is attacked far more than the game port.
FAQ#
Does fail2ban have a Minecraft filter?#
No. fail2ban 1.1.1 ships 96 filters and none of them is for Minecraft. You need a custom filter, such as the one on this page, that matches the lost connection: You are not white-listed on this server! line in logs/latest.log and captures the address after (/.
Can fail2ban stop bots joining my Minecraft server?#
It can stop bots that repeatedly try to join a whitelisted server, because each rejected attempt is logged with the client's address. With maxretry = 3, a bot gets three tries before it is blocked at the firewall for the ban time. It doesn't help on a public server without a whitelist, where bots can simply join.
Can fail2ban stop a Minecraft DDoS attack?#
No. A DDoS fills your connection or the server's network stack before Minecraft writes a log line, and it comes from too many addresses to ban one by one. Use DDoS protection from your host or a protected proxy. fail2ban only helps against a handful of addresses that keep failing to log in.
Where is the Minecraft server log file?#
In the logs folder of the server directory: logs/latest.log holds the current session, and older sessions are compressed to dated .log.gz files. In the itzg/minecraft-server Docker image the server directory is /data, so the log is /data/logs/latest.log inside the container.
Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.