Protecting a TeamSpeak 3 server from DDoS attacks

Published on 15 min read

Close the query port, tighten anti-flood, set a rate limit: what you can secure on a TeamSpeak 3 server yourself. And where those measures stop, because the line in front of it is already full.

A TeamSpeak server is the nerve center of a clan. Take it down and you do not just end a conversation, you end the practice session, the scrim or the raid. That is exactly why voice servers so often become a target, usually for someone from the immediate circle: a lost round, a ban, a feud between two clans.

This guide shows what you can secure yourself at no extra cost, where those measures reach their limit and what is left after that. The commands are written for Debian 12, Debian 13, Ubuntu 22.04 LTS and Ubuntu 24.04 LTS. Wherever root is required, it is stated.

Why voice servers in particular are attacked so often

Three sober reasons, and none of them has anything to do with the size of your project.

First, real-time voice is unforgiving. Nobody notices a website with two percent packet loss, because TCP simply sends the missing segments again. Voice has no retransmission: a lost packet is a gap in the audio, and everyone on the channel hears it immediately. So an attack does not have to saturate your line at all in order to make it unusable.

Second, the voice channel is connectionless. TeamSpeak carries voice over UDP. There is no handshake and no state before the first packet arrives. The source address can be forged at will, and your server has to look at every incoming packet before it can drop it. The attacker needs neither access nor a password, only your IP address and the port number. What happens on a technical level is described in the article What is a DDoS attack?.

Third, that very address is public. It sits in your Discord, on your website and, if enabled, in the TeamSpeak server list. A voice server nobody can find is useless. Anonymity is therefore not a protection strategy.

The default ports of TeamSpeak 3

Before you secure anything, you should know what is actually open. These are the factory settings of a TeamSpeak 3 server:

Port Protocol Direction Function
9987 UDP inbound Voice transmission (default_voice_port)
30033 TCP inbound File transfer (avatars, icons, channel files)
10011 TCP inbound ServerQuery in plain text (raw)
10022 TCP inbound ServerQuery over SSH
10080 and 10443 TCP inbound ServerQuery over HTTP and HTTPS respectively
41144 TCP inbound TSDNS, only needed if you run your own name resolution
2008 TCP outbound License and accounting service of TeamSpeak
2010 TCP outbound Listing in the public server list

The most important line of this table: the only thing that has to be reachable from the internet is 9987/UDP. Everything else is optional or belongs behind an access restriction.

What you can do yourself before spending any money

The following ten steps cost nothing and help against the attack types that hit a voice server most often: query floods, join spam and smaller UDP floods from a handful of sources.

1. Take stock: what is really listening

Rules for services that do not exist are harmless. One overlooked open port costs you the evening. As root, get an overview first:

ss -lntup

The interesting column is Local Address:Port. If it shows 0.0.0.0:10011 or [::]:10011, your ServerQuery access is reachable from the entire internet. If it shows 127.0.0.1:10011, it can only be reached locally and needs no firewall rule at all.

2. Close everything that is not needed

A firewall with a default rule of "drop everything inbound" is the foundation. Watch the order of the commands, otherwise you lock yourself out. The full procedure including the escape route is described in the guide Setting up the UFW firewall without locking yourself out. For a TeamSpeak server the result looks like this:

ufw allow 22/tcp
ufw allow 9987/udp
ufw allow 30033/tcp
ufw default deny incoming
ufw default allow outgoing
ufw enable

If you really do need ServerQuery access from outside, open it for your own address only. Replace 203.0.113.10 with your actual IP address:

ufw allow from 203.0.113.10 to any port 10011 proto tcp

Do not wall off outbound connections completely. Without access to the license and accounting service, the TeamSpeak server will not start up cleanly.

3. Take the ServerQuery port off the internet

ServerQuery access is the most underrated attack surface of a TeamSpeak server. Over port 10011 an attacker can work through credentials and fire off commands once per second. That is not a volumetric attack, it is a very frugal one that needs no botnet.

The cleanest approach is not to bind the query service to the outside at all. Open ts3server.ini in the server directory and set:

query_ip=127.0.0.1
query_protocols=raw
query_ip_allowlist=query_ip_allowlist.txt
query_ip_denylist=query_ip_denylist.txt
logquerycommands=1

The query service now listens on the server itself only, and you reach it through an SSH tunnel whenever you need it. What matters is that the file is actually read at startup. The start parameter for that is:

./ts3server_startscript.sh restart inifile=ts3server.ini

The file query_ip_allowlist.txt holds the addresses that are exempt from the flood control of the query service, query_ip_denylist.txt holds the blocked ones. Those are the file names from server version 3.12 onwards, older versions use query_ip_whitelist.txt and query_ip_blacklist.txt. Put only what belongs there into the allowlist, typically 127.0.0.1. Every additional address is an exception to exactly the protection you are switching on.

4. Tighten the flood control of the instance

The server ships with a brake of its own for ServerQuery. Log in as serveradmin without selecting a virtual server, and look at the current values first:

instanceinfo

You tighten them like this:

instanceedit serverinstance_serverquery_flood_commands=10 serverinstance_serverquery_flood_time=3 serverinstance_serverquery_ban_time=600

That allows ten commands in three seconds and then bans an address for ten minutes. If your server version additionally shows an upper limit for simultaneous query connections per address in the output of instanceinfo, set that one to a small value as well.

5. Never run query bots with the serveradmin account

Ranksystem, music bot, statistics script: almost every one of them runs with the full credentials of serveradmin. If the bot is compromised, or if its password sits in a public configuration file, the server belongs to somebody else.

Create a separate query login instead, bound to a specific client identity, and give that identity only the permissions the bot actually needs:

queryloginadd client_login_name=ranksystem cldbid=42

You find the database ID of your bot with clientdblist. The server prints the password once and never again.

6. Configure the anti-flood settings of the virtual server

Next to the brake on the instance, every virtual server has a point system of its own against command spam. Each command from a client costs points, and points are removed again every second. Two thresholds trigger a reaction: the first blocks further commands, the second bans the address. Select the virtual server and adjust the values:

use sid=1
serveredit virtualserver_antiflood_points_tick_reduce=5 virtualserver_antiflood_points_needed_command_block=150 virtualserver_antiflood_points_needed_ip_block=250

Those are the default values. If join and poke spam persists, lower the two thresholds step by step and watch the log. Values that are too aggressive hit your own members.

7. Raise the identity security level against automated join spam

Every TeamSpeak identity carries a security level that is produced through computing work. The server can demand a minimum level, and the factory default is level 8. Anyone who wants to generate throwaway identities in bulk has to compute for every single one:

serveredit virtualserver_needed_identity_security_level=10

This works against bot waves, but it comes at a price: existing members have to upgrade their identity once, and from around level 12 that takes uncomfortably long on weaker devices. Announce an increase in advance instead of setting it in the middle of prime time.

8. Server list, server password and real access restriction

The entry in the public server list makes your address discoverable in machine-readable form and does nothing for a closed clan server. Switch it off:

serveredit virtualserver_weblist_enabled=0

Be honest with yourself here: this removes one convenient way of finding your address, but it does not hide it. A scan across the address range finds an open UDP port 9987 anyway.

There are two ways to get a real access restriction. Inside TeamSpeak you set a server password and work with tokens for group assignment. At the network level, which is considerably stricter, you open 9987/UDP for known addresses only or run the voice server inside a VPN. For a fixed circle of ten people that is practical, for an open community it is not.

If you want to hand out a name instead of an IP address, use an SRV record in the form _ts3._udp.your-domain.com. The client resolves it on its own, port number included, and when the IP changes you only edit that one record.

9. Rate limiting on the host, with an honest assessment

On all four systems named above, the packet filter works with nftables under the hood. That lets you limit the packet rate per source address. Create a table of your own for it so that you do not have to touch an existing UFW configuration:

table inet ts3 {
    chain input {
        type filter hook input priority filter; policy accept;
        udp dport 9987 meter ts3flood { ip saddr limit rate over 400/second burst 800 packets } counter drop
    }
}

Save this as /etc/nftables.d/ts3.nft, create the directory beforehand if it does not exist, and load the file as root:

nft -f /etc/nftables.d/ts3.nft
nft list table inet ts3

You undo it with nft delete table inet ts3. After a reboot the table is gone, unless the file is included from /etc/nftables.conf.

For a sense of scale: a client that is speaking sends around 50 packets per second with 20-millisecond frames. So 400 per second leaves plenty of headroom for several people behind one connection, and the counter shows whether the rule ever took effect at all.

And now the honest assessment: against a distributed attack this rule barely helps. It counts per source address, and an attacker forges a new source address in every packet. It is good against individual troublemakers and against misconfigured clients. It is not DDoS mitigation.

10. Log things so that you have numbers when it counts

Once it starts, you need measurements and not the feeling that something is stuttering. You read the packet and error counters of the network card like this:

ip -s link show eth0

Run it twice ten seconds apart and take the difference: that gives you your packet rate. To see who currently holds connections to the voice port:

ss -uan 'sport = :9987'

A small sample of the incoming traffic comes from:

tcpdump -ni eth0 udp port 9987 -c 20

The log files of the server live in the logs/ subdirectory. With logquerycommands=1 from step 3, the query commands that were issued end up there too, which makes abuse visible after the fact. How to tell an attack apart from a configuration error is shown in the article Detecting a DDoS attack: how to tell for certain.

Where these measures stop

All ten steps have one thing in common: they only take effect once the packet is already there. For small disruptions that is entirely sufficient, for a volumetric attack it is not, and for a reason that has nothing to do with your configuration.

Do the math: an uplink with 1 Gbps carries around 125 megabytes per second and, with the smallest packets, about 1.49 million packets per second. At 10 Gbps that comes to around 14.9 million packets per second. That is the hard ceiling of the line, no matter what runs on the server.

One attack actually measured against a TeamSpeak server on port 9987 UDP came in at more than 473.4 Gbps and more than 41.5 million packets per second. That is roughly 470 times a 1 Gbps uplink and still almost three times the packet rate that a 10 Gbps uplink is able to carry at all.

What matters is where that traffic piles up: not on your network card, but on the line in front of it. Once that stretch is full, the packets from your members are lost right there, before your server ever gets to see them. A firewall rule in the operating system cannot relieve a line that ends before the operating system.

On top of that comes processing time. Even if your kernel could drop millions of packets per second, every one of those decisions costs CPU. A voice server that is busy throwing packets away sounds just as broken as one that no longer answers at all.

What KernelHost puts up against it

Tier 1: the always-on protection included with every server

On every KernelHost server, DDoS filtering runs permanently, at no extra charge and without you having to switch on or configure anything. It is built in two layers:

  • Layer 1: 17 Tbps of mitigation capacity in the global scrubbing network. Volumetric attacks are intercepted close to their source, long before they reach the datacenter. This is exactly the layer that relieves a line you cannot relieve yourself.
  • Layer 2: 3.2 Tbps of Arbor real-time filtering in the maincubes Premium Datacenter in Frankfurt am Main. Directly in front of the server, protocol-specific patterns are recognized and dropped packet by packet, among them UDP floods on the typical voice and game server ports.

Two points matter more than they sound. First, the protection is permanently active and does not have to kick in first, so there is no ramp-up window in which an attack gets through. Second, no attacked IP address is taken off the network: no null-routing means your members keep talking while the filtering runs. How this looks for other titles and protocols is described in the article Game server DDoS protection: why only real-time filtering helps.

Tier 2: Advanced DDoS Protection for projects under constant attack

Some projects are not hit once, they are hit for weeks. For those cases there is Advanced DDoS Protection from €50.00 per month, PrePaid and with no minimum term. It adds three things to the included always-on protection:

  • A dedicated protected IP from the Frankfurt core, which your server is switched over to. No rebuilding is needed on your side.
  • Self-managed protection rules per port and protocol in the customer panel. You configure the filtering for 9987/UDP differently from 30033/TCP without opening a ticket, and changes take effect in real time.
  • A protection profile matching the game or service in question, with ready-made profiles for more than 40 games and protocols, TeamSpeak included.

PrePaid means exactly that: no minimum term, no notice period, no contract, no setup fee. You add the protection for the duration of an attack wave and let it lapse afterwards.

The two tiers compared

Feature Included always-on protection Advanced DDoS Protection
Price included with every server at no extra charge from €50.00 per month, PrePaid
Setup none, active from the first minute ordered in the customer panel, dedicated protected IP
Capacity 17 Tbps of global scrubbing plus 3.2 Tbps of Arbor real-time filtering in Frankfurt am Main
Protection rules automatic profiles, maintained by our network team self-managed per port and protocol, changes take effect in real time
Game profile assigned automatically chosen by you, more than 40 games and protocols
Behavior during an attack no null-routing, the IP address stays reachable
Term part of the server package PrePaid, no minimum term, no notice period
Suitable for normal operation and occasional attacks projects that are targeted continuously

Common mistakes and how to fix them

The voice port is changed so that the attack runs into nothing: this works exactly until somebody runs a port scan, so usually a few minutes. At the same time, every member has to update their bookmarks. A different port only makes sense if you run several instances on one server anyway.

ServerQuery stays open because a bot needs it: as a rule the bot runs on the same server, and then query_ip=127.0.0.1 is enough. If it runs somewhere else, open the port for its static IP address only and create a restricted login for it with queryloginadd.

The firewall is armed and access is gone: on KVM root servers and dedicated servers from KernelHost you get onto the system through the VNC console in the customer panel. That console does not hang off the network stack of the guest system, so a firewall rule cannot block it. Log in there as root and switch the firewall off with ufw disable before you go looking for the cause.

The rate limit is set too tight and hits your own members: a typical case is a student dorm or a family behind one shared connection. To the filter that looks like a single address with a conspicuous number of packets. Check the counter with nft list table inet ts3: if it climbs while no attack is running, the value is too low.

The server no longer starts after the change to ts3server.ini: almost always the file was edited but not handed over at startup, or the other way round. Check both and look into the newest file under logs/, the cause is spelled out there in plain text.

Every measure is in place and the server is gone anyway: then you are dealing with a volumetric attack, and there is nothing left in your hands on the server itself. Collect the values from ip -s link together with the time of the first anomaly and pass both on to your provider. At KernelHost you open a ticket in the customer panel; while an attack is running you can also reach us through the WhatsApp emergency chat at +43 650 8209883.

Quick check for an emergency

  1. Measure instead of guessing: run ip -s link show eth0 twice and take the difference.
  2. Check that only 9987/UDP and 30033/TCP are open, and close the query port.
  3. Review the flood control of the instance and the anti-flood settings of the virtual server.
  4. If the line itself is saturated: save the numbers and the timestamp, then bring in your provider.

Frequently asked questions

My TeamSpeak server is unreachable right now. How do I tell whether it is an attack?
Read the counters of the network card with ip -s link show eth0 twice, ten seconds apart, and take the difference. If the number of received packets climbs far above the normal value while hardly anyone is connected, that points to an attack. If it stays unremarkable, the cause is more likely the service itself or the operating system.
Does it help to move the voice port from 9987 to a different port?
Barely. A port scan usually finds the new port within a few minutes, while every member has to update their bookmarks. As a stopgap it buys you a short break at best, as protection it is worth nothing.
Can I defend myself against an ongoing attack with nftables or iptables?
Against individual troublemakers yes, against a distributed attack no. A rate limit per source address does not bite when the source address is forged in every packet. Above all, every rule in the operating system takes effect only once the packet has already arrived. By then the line in front of it is already full.
The ServerQuery port 10011 is open. Can somebody take my server down through it?
Yes, and nobody needs a botnet for it. Over the query port an attacker can work through credentials and fire off commands once per second. Bind the service locally only with query_ip=127.0.0.1 in ts3server.ini and reach it through an SSH tunnel. If a bot needs it from outside, open the port exclusively for that static IP address.
Is there any point in taking the server out of the public server list?
It takes away one convenient way for attackers to find your address, but it does not hide it. Anyone who already knows the address keeps it, and a scan across the address range finds an open UDP port 9987 anyway. You can switch the listing off through ServerQuery with serveredit virtualserver_weblist_enabled=0.
Does KernelHost take my IP address off the network during an attack?
No. We do not use null-routing. The attacked IP address stays reachable, and only the malicious packets are dropped. The always-on protection is permanently active on every server and does not have to kick in first, so there is no ramp-up window at the start of an attack.
Is the included protection enough or do I need Advanced DDoS Protection?
For normal operation and occasional attacks the included always-on protection is enough, and it comes with every server at no extra charge: 17 Tbps of mitigation capacity in the global scrubbing network plus 3.2 Tbps of Arbor real-time filtering in Frankfurt am Main. Advanced DDoS Protection from €50.00 per month pays off when a project is targeted for weeks on end: a dedicated protected IP and protection rules that you manage yourself per port and protocol in the customer panel. PrePaid, with no minimum term.
I am under attack right now and I am not a customer yet. What do I do?
First secure the measurements: the packet rate from ip -s link, the affected IP address, the port and the time of the first anomaly. Your current provider can work with that. If they do not filter, or if they take your IP address offline, the only lasting fix is moving behind filtering in the network. While an attack is running you can reach us through the WhatsApp emergency chat at +43 650 8209883.

TeamSpeak TeamSpeak 3 server DDoS protection Voice server UDP flood ServerQuery Anti-flood Game server protection