Severe DDoS attack: what to do now

Published on 15 min read

Record the measurements, close ports, limit the query port and packet rates: what really helps during a sustained DDoS attack. And from which size onwards only the filtering in front of the server works.

An attack that is over after ten minutes is annoying. One that has been coming back every evening at the same time for days is something else: at that point your server is no longer a random target. This article shows what to do now, what you can secure yourself at no extra cost, and where those measures stop.

All commands refer to Debian 12, Debian 13, Ubuntu 22.04 LTS and Ubuntu 24.04 LTS and are written for root; as a regular user, put sudo in front of them. What a DDoS attack is in technical terms is explained in What is a DDoS attack?.

While the attack is running: do not reboot and do not rebuild half of your configuration. A reboot wipes exactly the counters you need for the report to your provider, and the attack comes back unchanged afterwards.

Why your server in particular is attacked so persistently

Servers that get hit for weeks on end almost always share the same three traits. First, they publish their address themselves: in a server list, in a Discord, through a DNS record. Second, their usage is tied to fixed hours, so an outage at 8 in the evening is as visible as it gets. Third, there is someone to whom that outage is worth something: a competing project, a banned player, an unhappy customer.

On top of that, many of the affected services run over UDP. UDP has no handshake you could insist on, and source addresses can be forged. An attacker therefore does not have to enter your service or even address it correctly in order to create load. With TCP services, half-open connections tie up resources instead, without ever being completed.

The ports this is actually about

An attack does not hit "the server", it hits a port. The overview below lists the default ports of the most frequently targeted services and doubles as your checklist: anything that does not appear here and is open anyway should be closed.

Service Default port
Minecraft Java Edition25565 TCP
Minecraft Bedrock Edition19132 UDP
FiveM and RedM30120 TCP and UDP
ARK: Survival Evolved7777 and 7778 UDP, Ascended only 7777 UDP
Rust28015 UDP, RCON 28016 TCP
Steam query port27015 UDP
TeamSpeak 39987 UDP, ServerQuery 10011 TCP
Web server80 and 443 TCP
Pterodactyl Wings8080 TCP, SFTP 2022 TCP
Remote accessSSH 22 TCP, RDP 3389 TCP
DatabaseMariaDB 3306 TCP, PostgreSQL 5432 TCP
VPNOpenVPN 1194 UDP, WireGuard 51820 UDP

A second group does not show up as a target but as a source: 53 (DNS), 123 (NTP), 389 (CLDAP), 1900 (SSDP), 11211 (memcached) and 27015 as well. If these source ports dominate, you are dealing with a reflection and amplification attack. The senders are then uninvolved, badly configured servers, which is why blocking individual addresses leads nowhere.

What you can do yourself before spending money

This section is the longest one, and deliberately so: a properly configured server withstands small and medium attacks on its own and delivers the measurements for the next stage when things get serious.

1. The first minutes: measure instead of tinker

Before you change anything, record what is happening right now. Four values are enough: the incoming packet rate, the state distribution of the connections, kernel messages, and the question of whether the service still responds locally.

IF=$(ip -o route get 1.1.1.1 | awk '{print $5}')
A=$(cat /sys/class/net/$IF/statistics/rx_packets) || exit 1; sleep 1; B=$(cat /sys/class/net/$IF/statistics/rx_packets); echo "$((B-A)) packets/s incoming on $IF"
ss -Htan | awk '{print $1}' | sort | uniq -c | sort -rn
dmesg -T | tail -50
curl -o /dev/null -s -w '%{time_total}\n' http://127.0.0.1/

The interface name comes from the default route, because eth0 does not exist at all on many KVM servers (there it is called ens3 or enp1s0), and a wrongly entered name will happily report zero packets. A large block in SYN-RECV is the signature of a SYN flood. If the service responds quickly over 127.0.0.1 while it fails from the outside, the problem sits in the network. And note: on the server you only measure what got through, behind a filtering layer you see just a fraction of the volume. The systematic approach is covered in Detecting a DDoS attack.

If you can no longer get through over SSH, that is not a reason to reboot, it is the consequence of a saturated uplink. Access then runs through the VNC console in the customer panel, which works independently of the network connection.

2. Taking stock: what is listening to the outside?

ss -lntup
nmap -Pn -p- --min-rate 1000 YOUR.SERVER.IP.ADDRESS

The first command shows your own view, the second one, run from another machine, shows the view of the attacker. What matters is the local address: 0.0.0.0:3306 means "reachable from the entire internet", 127.0.0.1:3306 means "local only" and needs no firewall rule. Forgotten services turn up regularly in the process: a test instance, an admin panel, a database without a local binding.

3. Leave open only what the service really needs

The order matters, otherwise you lock yourself out: first the allow rules, then the default policy, then switch it on.

ufw allow 22/tcp comment 'SSH'
ufw allow 80,443/tcp comment 'Web'
ufw allow 25565/tcp comment 'Game port'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
ufw status verbose

Afterwards, use a second, freshly opened session to check whether you still get in: existing connections survive the activation even when the matching rule is missing. The full guide, including how to get back in, is in Setting up the UFW firewall without locking yourself out.

The database does not belong on the open internet, so /etc/mysql/mariadb.conf.d/50-server.cnf has to contain bind-address = 127.0.0.1. Admin interfaces do not belong there either: without a fixed IP address for a targeted allow rule, leave the port closed and reach it through an SSH tunnel.

ssh -N -L 8443:127.0.0.1:8443 root@YOUR.SERVER.IP.ADDRESS

Published container ports bypass the UFW chains. Bind them locally, so -p 127.0.0.1:8080:80 instead of -p 8080:80, and route access through a reverse proxy.

4. Securing the query port without closing it

Besides the actual port, many services have a second one that hands out status information: 27015 UDP for Steam-based games, the query port for Minecraft, the ServerQuery interface on 10011 TCP for TeamSpeak. They answer without a login, and the answer is larger than the request, which makes them usable for amplification against third parties. Closing them is usually not an option, because your server then disappears from the server list. Limit them per source address instead:

iptables -I INPUT -p udp --dport 27015 -m hashlimit --hashlimit-name query --hashlimit-mode srcip --hashlimit-above 10/sec --hashlimit-burst 20 -j DROP

Ten queries per second per address are enough for real players and for your monitoring, while a source with thousands of requests per second gets dropped. With Minecraft, enable-query=false in server.properties switches the query port off entirely without the server disappearing from the list. enable-status=false additionally suppresses the reply to the list ping, but then your server shows up as offline. Bind the TeamSpeak ServerQuery interface to 127.0.0.1.

5. Limiting connection and packet rates

iptables -I INPUT -p tcp --dport 443 --syn -m connlimit --connlimit-above 40 --connlimit-mask 32 -j DROP
iptables -I INPUT -p udp --dport 7777 -m hashlimit --hashlimit-name game_udp --hashlimit-mode srcip --hashlimit-above 600/sec --hashlimit-burst 900 -j DROP

The first rule drops new TCP connections as soon as one address has more than forty of them open at the same time, the second drops UDP packets once the same source sustains more than 600 packets per second. Both figures are starting points, not truths: set them too tightly and you throw out your own users. Use iptables -L INPUT -n -v to check whether the hit counters are rising. If they stay at zero, the rule is never reached.

Plain iptables rules are gone after a reboot; you persist them with apt-get install -y iptables-persistent and netfilter-persistent save. Under UFW they belong in /etc/ufw/before.rules, otherwise they disappear on the next ufw reload. And then there is the fundamental limit: limits per source address only work as long as there are conspicuous sources. If each of the 200,000 addresses involved sends exactly one packet, none of them stands out.

6. Preparing the kernel for many small packets

sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
sysctl -w net.core.somaxconn=4096
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

SYN cookies answer the handshake without allocating memory and are active out of the box. The two queues are the first to overflow when many connection attempts arrive at once. For a permanent setting, such values belong in a file under /etc/sysctl.d/ and are applied with sysctl --system. The last line shows connection tracking, which only exists with an active firewall: once its table fills up, the server drops legitimate packets as well and logs nf_conntrack: table full, dropping packet.

7. Application level: rate limits, whitelist, plugins and anti-cheat

Attacks that overload the application instead of the uplink look different: few packets, but expensive ones. An HTTP flood against the search function of a shop does not need a gigabit. On a web server, a limit per address is therefore the most effective measure, in nginx in the http block and afterwards in the server or location block:

limit_req_zone $binary_remote_addr zone=web:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=webconn:10m;

limit_req zone=web burst=20 nodelay;
limit_conn webconn 20;

Against repeated login attempts, Fail2ban is a sensible addition; the setup is described in Set up fail2ban. On game servers, three things work against everything that uses the regular way in: a whitelist (with Minecraft whitelist=true together with enforce-whitelist=true), a realistic cap on concurrent players, and network events validated on the server side.

What plugins and anti-cheat systems cannot do deserves to be said just as plainly: both run in the same process as the game and only check once a packet is already being processed. If the process stalls, the protection logic stalls with it. The same applies to the whitelist, because whoever floods your server does not want to join at all.

8. Server list, DNS and your own IP address

Honesty beats wishful thinking here: your IP address cannot be kept secret. Everyone who has connected once knows it, and a list entry publishes it anyway. Two habits still help: never publish the raw address yourself, not in a Discord channel and not through a status bot, and let your users connect through a hostname so that an address change does not break every reference.

During the change itself, old DNS records are the classic mistake: a forgotten A record, a subdomain left over from a status page, an MX record pointing at the same server. And even then, an address change buys time rather than solving anything.

9. Logging while the incident is running

Without a baseline you cannot say afterwards whether 40,000 packets per second was a lot or simply a Tuesday evening. With apt-get install -y vnstat sysstat the measurement keeps running in the background. During the incident, collect the following:

mkdir -p /root/incident && cd /root/incident
date -u > 01-time.txt
ss -s > 02-sockets.txt
ip -s link > 03-interfaces.txt
sar -n DEV 1 10 > 04-packetrate.txt
dmesg -T | tail -100 > 05-kernel.txt
tcpdump -ni "$IF" -s 96 -c 500 -q > 06-sample.txt

Always limit tcpdump with -c, because an unlimited capture on a saturated uplink puts additional load on the server. The ticket should then contain the time with the time zone, the affected IP address together with the port, the measured packet rate with the direction, the protocol distribution and the conspicuous source ports. With that information a report is acted on immediately; with "the server was slow" you get follow-up questions.

Where these measures stop

Now for the part that no configuration file solves. Every measure so far operates at the far end of the uplink. A firewall rule decides the fate of a packet that has already crossed the wire: you can drop it, but you cannot unsend it.

Do the math once. An uplink of 1 Gbps carries 125 megabytes per second and is full as soon as somebody sends more. With the smallest possible packets that corresponds to roughly 1.49 million packets per second, and at 10 Gbps to roughly 14.9 million. Depending on CPU and network card, a server kernel processes a few hundred thousand of them before it starts dropping. So an attack that does not even fill a third of your uplink still takes you down, because the processing time goes into the dropping.

For a sense of the magnitudes that really occur: on KernelHost servers we have filtered in real time, among others, an attack of over 473.4 Gbps at over 41.5 million packets per second against a voice server (9987 UDP), and a UDP flood of over 112.2 Gbps against a game server (7777 UDP). The first case is roughly 473 times what an uplink of 1 Gbps can carry at all. Volumetric attacks have to end in the network in front of the server, otherwise they end in your uplink.

What KernelHost puts up against it

The always-on protection included with every server

DDoS protection at KernelHost is built in two layers and permanently active, with nothing for you to order, switch on or configure:

  • Layer 1: 17 Tbps of mitigation capacity in the global scrubbing network. Volumetric attacks are scrubbed close to their source, before they reach the datacenter.
  • Layer 2: Arbor real-time filtering with 3.2 Tbps on site in Frankfurt am Main. Directly in front of the server, protocol-specific patterns are detected and dropped, packet by packet.

Two properties make the difference when it counts. The protection runs permanently and does not have to react to an attack first, so there are no opening minutes in which the server is gone. And no null-routing is used: your IP address stays on the network, only the malicious packets are dropped. If a provider instead takes the attacked address off the network, the result for you is identical to a successful attack. The datacenter is maincubes in Frankfurt am Main, Germany, and the operator is KernelHost GmbH, based in Vienna, Austria. The protection is included in every server package at no surcharge, from the KVM root server to the dedicated server.

Advanced DDoS Protection for projects under constant fire

Some projects are attacked not occasionally, but deliberately and for weeks on end. For those there is Advanced DDoS Protection from €50.00 per month, PrePaid and with no minimum term. The difference is not more capacity, it is control:

  • A dedicated protected IP from the Frankfurt core, which your server is switched over to inside our own network. Nothing has to be rebuilt on your side.
  • Self-managed protection rules per port and protocol in the customer panel, without a ticket: the game port gets different rules than the query port.
  • Changes take effect in real time, so you can fine-tune in the middle of an ongoing attack.
  • A protection profile that matches the game, plus profiles for custom and modified applications on any TCP or UDP port.

The two tiers compared

Feature Included always-on DDoS protection Advanced DDoS Protection
Price included in every server package, at no surcharge from €50.00 per month, PrePaid
Filtering capacity 17 Tbps of global scrubbing plus Arbor real-time filtering with 3.2 Tbps in Frankfurt am Main the same two-layer filtering
IP address the IP address of your server an additional dedicated protected IP
Rule set automatic profiles, no configuration needed your own rules per port and protocol in the customer panel
Changes are applied automatically take effect in real time, even during an attack
Term tied to the server package PrePaid, no minimum term, no notice period, no setup fee

For most projects the included always-on protection together with a clean server configuration is enough. Advanced DDoS Protection is the answer to somebody taking it personally.

Common mistakes and how to fix them

"I rebooted the server and it ran again for a short while": that was the attack arriving in waves, not the reboot. Reboots wipe the counters you would have needed for the report.

"I changed the IP address and was offline again two hours later": the new address came from the same source as the old one, usually a list entry, a status bot or an old DNS record.

"I block the conspicuous addresses and new ones keep coming": in a distributed attack there are tens of thousands of them, and with reflection attacks you are only blocking uninvolved third parties anyway.

"My firewall rules have no effect": three causes are common: the rules sit behind the UFW chains, they were gone after the last reboot, or the attack is volumetric and the rule works correctly on an uplink that is already full.

"The utilization was low and the service was gone anyway": a typical packet-rate attack. The bandwidth looks harmless, the number of packets does not. Measure packets per second, not megabits.

"I do not see anything unusual in tcpdump": if the traffic is filtered in the network upstream, nothing arrives on the server, exactly as expected. If the uplink is saturated, on the other hand, even the SSH session may no longer reach you. Use the VNC console in the customer panel then.

In short

Measure first, rebuild afterwards. Close everything the service does not need, limit the query port, the connections and the packet rates per source address, and collect measurements while the incident is running. That covers you against everything that gets by without serious bandwidth. Beyond that, only the network in front of the server decides the outcome.

If your project already runs at KernelHost, the filtering is active without you having to do anything. If you still notice something unusual, open a support ticket so that we can fine-tune the filter rules for your IP address. During an ongoing attack you can also reach us through the WhatsApp emergency chat at +43 650 8209883.

Frequently asked questions

My server has been unreachable for hours. What do I check first?
The incoming packet rate on the network card, the state distribution of the connections with ss, and the question of whether the service still responds quickly over 127.0.0.1. If it answers locally in milliseconds while it fails from the outside, the problem sits in the network and not in the application. A large block in SYN-RECV is the signature of a SYN flood.
Should I reboot the server or change the IP address?
Neither usually achieves much. A reboot wipes exactly the counters you need for the report to your provider, and the attack comes back unchanged afterwards. Changing the address buys time rather than solving anything: within a few hours the new address is usually back in the same server list, in the same status bot or in an old DNS record.
I can no longer reach the server over SSH. How do I still get in?
Once the uplink is saturated, SSH does not get through either, which is normal and not a defect. Use the VNC console in the customer panel, which works independently of the network connection of the system. Do not reboot the server because of it.
Why does my firewall no longer help against a large attack?
Because it can only drop what has already arrived. With the smallest possible packets, an uplink of 1 Gbps is saturated at around 1.49 million packets per second. One attack filtered in practice ran at over 473.4 Gbps and over 41.5 million packets per second. The rule works correctly, the uplink is full regardless. Volumetric attacks have to be filtered in the network in front of the server.
Can a plugin or an anti-cheat stop the attack?
No. Both run in the same process as the application and only check once a packet is already being processed. If the process stalls, the protection logic stalls with it. They are valuable against cheaters and troublemakers, but useless against attacks on availability. The same applies to a whitelist, because whoever floods your server does not want to join at all.
Is my IP address taken offline during an attack?
Not at KernelHost. No null-routing is used. The attacked IP address stays on the network, only the malicious packets are dropped, and the connections of real users keep running. If a provider instead takes the address off the network, the result for you is identical to a successful attack.
Does DDoS protection at KernelHost cost extra?
No. Every server runs two-layer always-on protection at no surcharge: a global scrubbing network with 17 Tbps of mitigation capacity and Arbor real-time filtering with 3.2 Tbps on site in the maincubes datacenter in Frankfurt am Main. It is permanently active, so there is nothing for you to switch on or configure.
When do I additionally need Advanced DDoS Protection?
When your project is attacked deliberately and for weeks on end, for example every evening at the same time and with changing patterns. You get a dedicated protected IP for it and manage the protection rules yourself in the customer panel, separated by port and protocol, with a protection profile that matches the game. Changes take effect in real time. The price starts at €50.00 per month, PrePaid and with no minimum term.

DDoS attack Emergency Packet rate iptables UFW Server administration Advanced DDoS Protection Null-routing