JoinGG

Custom Domains and SRV Records for Game Servers

DNS cannot point at a port, which is why game server addresses are awkward. What an SRV record does, which games read one, and how to move hosts invisibly.

SweetMask · 9 min read

You have typed the same fifteen characters into Discord four times this week. One person has them on a sticky note. One scrolls back to a message from March every time they want to play. One keeps entering 25577 as 25775 and then tells you the server is down. And when you move host in three months, every one of those copies goes wrong at the same moment, quietly, and the only signal you get is that people stop showing up.

A domain fixes that, and not for the reason most owners think. play.example.com does look better than 203.0.113.10:25577 on a listing, but memorability is the small half of it. The large half is that a name is a layer of indirection you own, and an IP address is one you rent from whoever is hosting you this year.

Nearly every problem people hit setting this up comes from one of two beliefs: that DNS can point at a port, or that propagation is a mysterious delay being done to them. Here is the order to get it right in.

1. An A record maps a name to an address, and that is all it can do

An A record answers exactly one question: what IPv4 address does this name have? AAAA answers it for IPv6. That is the entire contents of the answer. There is no port field, because DNS was never a service-location system — it maps names to hosts, and which door you knock on was left to the application.

So play.example.com → 203.0.113.10 is a complete, working setup if your server is on the game's default port, because the client fills that in for you. Minecraft Java assumes 25565, Source-engine games assume 27015, FiveM assumes 30120. If you are on the default, buy a domain, add an A record, and you are done — everything below is for people who are not.

A CNAME does not help either. It aliases one name to another name, and the resolver then chases that name's A record. Still no port.

That gap is worth stating plainly, because everything awkward about game server addresses follows from it:

DNS answers the question what address, not the question which socket. The port has to be carried by something else, or typed by a human.

2. SRV is the record that carries a port, and this is its exact shape

RFC 2782 defined a record type that does encode one. In zone-file form:

_service._proto.name  TTL  IN  SRV  priority  weight  port  target

For a Minecraft Java server on a non-default port, the real thing:

_minecraft._tcp.example.com.  300  IN  SRV  0  5  25577  mc.example.com.

_service is the symbolic service name and _proto the transport; both carry a leading underscore specifically so they cannot collide with a real hostname. name is the domain the record applies to — what a player types. priority works like MX, lowest reachable first. weight splits load between records sharing a priority. port is the one you came for. target is the host that answers.

Most registrar panels never show you that line. They split it into boxes labelled Service, Protocol, Name, Priority, Weight, Port and Target, then differ on whether Name means _minecraft._tcp.example.com, or _minecraft._tcp, or just a subdomain — and some append your domain automatically, which is how people end up with a record sitting at _minecraft._tcp.example.com.example.com. Save it, then read back what the panel says it stored. That check catches the most common failure in this exercise.

3. Whether anyone reads your SRV record depends on the game

This is where a guide written for one game gets copied into another and stops being true. An SRV record is inert unless the client goes looking for it.

Minecraft Java does look, at _minecraft._tcp.<name>. One condition matters: the lookup only happens when the player types a bare hostname. Enter example.com:25577 with an explicit port and the client uses what you gave it and never queries SRV at all — which is why "it works for me" from the owner who has the port saved in their server list tests nothing.

Minecraft Bedrock does not. Address and port are separate boxes filled in by hand, so if you run Geyser alongside Java, your Bedrock players need the port told to them however tidy your Java DNS is.

Source-engine games — CS2, Team Fortress 2, Garry's Mod, Left 4 Dead 2 — do not. Neither the Steam server browser nor the connect console command performs an SRV lookup; it has been asked for and never shipped. connect play.example.com resolves the A record and assumes 27015. On any other port your players type connect play.example.com:27016, and the domain's job shrinks to being the memorable half of that string.

FiveM is the odd one out. Its client does resolve a _cfx._udp.<name> record, so it can be made to work — but it matters less than owners expect, because most people never type your address. They join from the in-game list or a cfx.re/join link, both of which use the endpoints the server advertised when it registered. Your DNS is not in that path at all.

So, committing: if you run Minecraft Java on a non-default port, set the SRV record — it is the intended mechanism and it works. For everything else, put the server on the game's default port if you possibly can and use a plain A record. Where you cannot, stop fighting DNS and publish the port beside the hostname, in your Discord topic and in the description of your directory listing. A port people can copy beats a record their client will not read, which is also why the anatomy of a server address is worth knowing.

4. The target must be a real hostname, and must not be a CNAME

RFC 2782 is blunt about the target field: the name must have one or more address records, and it must not be an alias. Two rules fall out of that, and people break both.

You cannot put an IP in the target. SRV 0 5 25577 203.0.113.10 is not valid — the target is a domain name, and a resolver will try to look up 203.0.113.10 as a name and fail. Create an A record first, mc.example.com → 203.0.113.10, and point the SRV at that.

You also cannot point the target at a CNAME. Hosts often hand you a name like node47.hostingcompany.net that is itself a CNAME to something else; strict clients refuse it, forgiving ones chase it anyway, and you get a server that works for some players and not others — a much worse failure than not working at all.

The two-hop structure this forces is the point, not an annoyance:

_minecraft._tcp.play.example.com  →  SRV  →  mc.example.com  →  A  →  203.0.113.10

Migrating host means changing one A record. The SRV never moves. Nobody's shortcut breaks.

One provider trap: on Cloudflare these records must be grey-cloud, DNS-only. The orange-cloud proxy handles HTTP and HTTPS, not the Minecraft protocol, and leaving it on produces a name that resolves perfectly and refuses every connection.

One further consequence, if you run Velocity or BungeeCord in front of a network: when a client follows an SRV record, the hostname it sends in the handshake is the target, not what the player typed. A forced host keyed on survival.example.com will never match if players arrive via an SRV pointing at node1.example.com, so give each front door its own record and its own target name. Paper, Purpur and Fabric behave differently behind one, too.

5. TTL is the only control you have over a migration

TTL is how many seconds a resolver may keep an answer before asking again. Registrar defaults are usually 3600, sometimes a day.

That number is the whole trick to an invisible host move. At least one full TTL before migrating, drop the TTL on the records you are about to change to 300, or 60. Wait out the old value so every cached copy of it has expired. Now move, change the A record, and the world catches up inside a minute. Put the TTL back up a day or two later. Do it in the other order — move first, then discover the old record is cached for a day — and you get precisely the outage the domain was meant to prevent.

Which disposes of "propagation". Nothing propagates and no update is pushed anywhere. Your authoritative nameserver has the new value the instant you save it, and every resolver learns it the next time it asks, which is when its cache expires. TTL is the only variable.

The one real wrinkle is negative caching, defined in RFC 2308: when a resolver is told a name does not exist, it caches that answer too, for a duration taken from the zone's SOA record. So if you tested the address before creating the record — everyone does — you may have taught your own resolver that it does not exist, and it will keep saying so afterwards. That is the explanation behind most "I made the record and it still doesn't work" reports, and it clears itself.

6. Verify from outside your own network

Your own machine is the worst place to test from: a resolver you configured, a cache holding your mistakes, possibly a hosts file entry from two years ago. Query a public resolver by name instead.

dig SRV _minecraft._tcp.example.com @1.1.1.1
nslookup -type=srv _minecraft._tcp.example.com 1.1.1.1

dig is standard on Linux and macOS; nslookup is the one already installed on Windows. A correct answer returns the four numbers and the target, ending in a dot. Then check the target separately with dig A mc.example.com @1.1.1.1, because an SRV pointing at a name with no address record looks perfectly healthy in the first query and fails in the second.

Finally, verify the thing DNS cannot tell you: that something is listening. A correct record aimed at a closed port is still a correct record. If the name resolves and nothing answers, the problem has moved to the network, usually a firewall or a port that was never forwarded. Confirm reachability by querying the server the way a server list does — over the game's own protocol, from outside your LAN, which is what this site does to every listing on the schedule set out in our methodology.

7. The domain is insurance, and that is what you are paying for

Your address is scattered across places you do not control: pinned Discord messages, other people's server lists, a friend's notes app. When your host raises prices, or the route to your players degrades and a different region becomes the answer, or the IP starts attracting attention you did not ask for, an IP-addressed server has to reissue itself to every player individually and loses everyone who never notices. A named server changes one record.

Give the name the same care as the server name itself — short, typeable, obviously yours, and not stuffed with tags. Then use it everywhere: MOTD, Discord, your entry on the Minecraft directory. Once it is the only address anyone has learned, the address underneath it becomes something you can change on a Tuesday afternoon without telling a soul.

Common questions

Can I use an SRV record to hide the port on a CS2 or TF2 server?
No, Source-engine games like CS2 and Team Fortress 2 do not perform SRV lookups. Neither the Steam server browser nor the connect console command checks for an SRV record. Players must type the port alongside the domain if your server uses anything other than the default 27015 port.
Why does a game server connection fail when routed through Cloudflare proxy?
The Cloudflare orange-cloud proxy only handles HTTP and HTTPS traffic, not game protocols like Minecraft's. Enabling the proxy creates a name that resolves properly but refuses every server connection. Your records must be set to grey-cloud DNS-only mode to work.
Why does a newly created domain record fail to resolve on my computer?
Testing the domain before creating the record causes your resolver to cache the non-existent response. Defined in RFC 2308 as negative caching, resolvers hold this negative result for a duration set by the zone's SOA record. The issue will clear itself once the cached record expires.
Tags
dnssrv recordsserver administrationhostingminecraft
Share

SweetMask

Published · 9 min read

All articles

Keep reading