[networking] How to redirect DNS to different ports

(It's been a while since I did this stuff. Please don't blindly assume that all the details below are correct. But I hope I'm not too embarrassingly wrong. :))


As the previous answer stated, the Minecraft client (as of 1.3.1) supports SRV record lookup using the service name _minecraft and the protocol name _tcp, which means that if your zone file looks like this...

arboristal.com.                 86400 IN A   <your IP address>
_minecraft._tcp.arboristal.com. 86400 IN SRV 10 20 25565 arboristal.com.
_minecraft._tcp.arboristal.com. 86400 IN SRV 10 40 25566 arboristal.com.
_minecraft._tcp.arboristal.com. 86400 IN SRV 10 40 25567 arboristal.com.

...then Minecraft clients who perform SRV record lookup as hinted in the changelog will use ports 25566 and 25567 with preference (40% of the time each) over port 25565 (20% of the time). We can assume that Minecraft clients who do not find and respect these SRV records will use port 25565 as usual.


However, I would argue that it would actually be more "clean and professional" to do it using a load balancer such as Nginx. (I pick Nginx just because I've used it before. I'm not claiming it's uniquely suited to this task. It might even be a bad choice for some reason.) Then you don't have to mess with your DNS, and you can use the same approach to load-balance any service, not just ones like Minecraft which happen to have done the hard client-side work to look up and respect SRV records. To do it the Nginx way, you'd run Nginx on the arboristal.com machine with something like the following in /etc/nginx/sites-enabled/arboristal.com:

upstream minecraft_servers {
    ip_hash;
    server 127.0.0.1:25566 weight=1;
    server 127.0.0.1:25567 weight=1;
    server 127.0.0.1:25568 weight=1;
}
server {
    listen 25565;
    proxy_pass minecraft_servers;
}

Here we are controlling the load-balancing ourselves on the server side (via Nginx), so we no longer need to worry that badly behaved clients might prefer port 25565 to the other two ports. In fact, now all clients will talk to arboristal.com:25565! But the listener on that port is no longer a Minecraft server; it's Nginx, secretly proxying all the traffic onto three other ports on the same machine.

We load-balance based on a hash of the client's IP address (ip_hash), so that if a client disconnects and then reconnects later, there's a good chance that it'll get reconnected to the same Minecraft server it had before. (I don't know how much this matters to Minecraft, or how SRV-enabled clients are programmed to deal with this aspect.)

Notice that we used to run a Minecraft server on port 25565; I've moved it to port 25568 so that we can use port 25565 for the load-balancer.

A possible disadvantage of the Nginx method is that it makes Nginx a bottleneck in your system. If Nginx goes down, then all three servers become unreachable. If some part of your system can't keep up with the volume of traffic on that single port, 25565, all three servers become flaky. And not to mention, Nginx is a big new dependency in your ecosystem. Maybe you don't want to introduce yet another massive piece of software with a complicated config language and a huge attack surface. I can respect that.

A possible advantage of the Nginx method is... that it makes Nginx a bottleneck in your system! You can apply global policies via Nginx, such as rejecting packets above a certain size, or responding with a static web page to HTTP connections on port 80. You can also firewall off ports 25566, 25567, and 25568 from the Internet, since now they should be talked to only by Nginx over the loopback interface. This reduces your attack surface somewhat.

Nginx also makes it easier to add new Minecraft servers to your backend; now you can just add a server line to your config and service nginx reload. Using the old port-based approach, you'd have to add a new SRV record with your DNS provider (and it could take up to 86400 seconds for clients to notice the change) and then also remember to edit your firewall (e.g. /etc/iptables.rules) to permit external traffic over that new port.

Nginx also frees you from having to think about DNS TTLs when making ops changes. Suppose you decide to split up your three Minecraft servers onto three different physical machines with different IP addresses. Using Nginx, you can do that completely via config changes to your server lines, and you can keep those new machines inside your firewall (connected only to Nginx over a private interface), and the changes will take effect immediately, by definition. Whereas, using SRV records, you'll have to rewrite your zone file to something like this...

arboristal.com.                 86400 IN CNAME mc1.arboristal.com.
mc1.arboristal.com.             86400 IN A   <a new machine's IP address>
mc2.arboristal.com.             86400 IN A   <a new machine's IP address>
mc3.arboristal.com.             86400 IN A   <a new machine's IP address>
_minecraft._tcp.arboristal.com. 86400 IN SRV 10 20 25565 mc1.arboristal.com.
_minecraft._tcp.arboristal.com. 86400 IN SRV 10 40 25565 mc2.arboristal.com.
_minecraft._tcp.arboristal.com. 86400 IN SRV 10 40 25565 mc3.arboristal.com.

...and you'll have to leave all three new machines poking outside your firewall so that they can receive connections from the Internet. And you'll have to wait up to 86400 seconds for your clients to notice the change, which could affect the complexity of your rollout plan. And if you were running any other services (such as an HTTP server) on arboristal.com, now you have to move them to the mc1.arboristal.com machine because of how I did that CNAME. I did that only for the benefit of those hypothetical Minecraft clients who don't respect SRV records and will still be trying to connect to arboristal.com:25565.


So, I think both ways (SRV records and Nginx load-balancing) are reasonable, and your choice will depend on your personal preferences. I caricature the options as:

  • SRV records: "I just need it to work. I don't want complexity. And I know and trust my DNS provider."
  • Nginx: "I foresee arboristal.com taking over the world, or at least moving to a bigger machine someday. I'm not scared of learning a new tool. What's a zone file?"

Examples related to networking

Access HTTP response as string in Go Communication between multiple docker-compose projects Can't access 127.0.0.1 How do I delete virtual interface in Linux? ConnectivityManager getNetworkInfo(int) deprecated Bridged networking not working in Virtualbox under Windows 10 Difference between PACKETS and FRAMES How to communicate between Docker containers via "hostname" java.net.ConnectException: failed to connect to /192.168.253.3 (port 2468): connect failed: ECONNREFUSED (Connection refused) wget: unable to resolve host address `http'

Examples related to dns

How is VIP swapping + CNAMEs better than IP swapping + A records? ping: google.com: Temporary failure in name resolution What's the source of Error: getaddrinfo EAI_AGAIN? How do I solve the "server DNS address could not be found" error on Windows 10? Creating self signed certificate for domain and subdomains - NET::ERR_CERT_COMMON_NAME_INVALID How to force DNS refresh for a website? ssh: Could not resolve hostname [hostname]: nodename nor servname provided, or not known How to filter wireshark to see only dns queries that are sent/received from/by my computer? How can I list ALL DNS records? How to redirect DNS to different ports

Examples related to minecraft

How to redirect DNS to different ports Java Could not reserve enough space for object heap error Launch Minecraft from command line - username and password as prefix