Arma Reforger's dedicated server uses three UDP ports, and the documentation lists them without saying which ones matter. In practice one is mandatory, one decides whether anyone can find your server, and one should almost never be reachable from the internet at all.
The distinction matters because the failure modes look nothing alike, and only one of them looks like a networking problem.
The three ports
All three are UDP. None of them is TCP, and a firewall rule written for TCP out of habit will pass no game traffic whatsoever while the server appears to start perfectly.
| Port | Purpose | Required |
|---|---|---|
| 2001 | Game traffic (client connections) | Yes |
| 17777 | Steam / A2S query (server browser listing) | Only to be findable |
| 19999 | RCON (remote administration) | No, and prefer not |
What the config actually says
The server reads a JSON file, conventionally config.json, passed to the binary at
launch. The port-relevant part is small:
{
"bindAddress": "",
"bindPort": 2001,
"publicAddress": "",
"publicPort": 2001,
"a2s": {
"address": "",
"port": 17777
}
}
Two pairs, and the difference between them is the thing people get wrong.
bindAddress and bindPort are the socket the server listens on, locally. An
empty bindAddress means all interfaces, which is what you want on a dedicated box.
publicAddress and publicPort are what the server tells the backend to
advertise. On a machine with a public IP and no NAT they are the same numbers, and
leaving them empty lets the server work them out. Behind NAT or inside a container
they are not the same thing, and that is where a working server becomes an
unreachable one.
The failure modes are not interchangeable
This is the part worth internalising, because it turns a vague "the server is broken" into a specific port to check.
2001 closed. Nobody connects, at all. Direct connect fails, browser connect fails. The server log shows the process healthy and waiting. If any player can connect, 2001 is fine and the problem is elsewhere.
17777 closed. The server runs perfectly and is invisible. It does not appear in the Steam browser, but anyone you give the address to can still connect directly. That asymmetry is the diagnostic: if direct connection works and the browser listing does not, it is the query port, not the game port.
19999 closed. Nothing at all happens, unless you were trying to use RCON. Which is the correct state for it.
Behind NAT or in a container
The two-pair distinction above stops being academic the moment the server is not directly on a public IP.
Inside Docker, bindAddress is on the container's network namespace, not the
host's. Publishing the port maps host traffic to it, but the server still
advertises whatever publicPort says, and if you have published host 2101 to
container 2001, the backend needs to hear 2101 or every browser listing points at a
port nothing is listening on.
The rule that avoids the whole class of problem: publicPort must be the port the
outside world reaches, and bindPort must be the port the process itself opens.
When those are the same number, you can leave both alone. When they are not, only
one of them is a guess.
Checking it rather than assuming it
From another machine, not from the server itself. A local test passes through no firewall and tells you nothing:
# Is anything listening locally?
ss -lunp | grep -E '2001|17777'
# Is the query port answering from outside?
nmap -sU -p 17777 your.server.address
UDP scanning is slower and less definitive than TCP, because there is nothing to
refuse a connection with: an open port and a silently dropped packet can look the
same. Treat open|filtered as inconclusive and confirm from the game itself.
The faster real-world check is the asymmetry above: try a direct connection. If it works, 2001 is open and you are debugging 17777.