Hosting your own Minecraft server without a public IP adress.

Roman Pochylý
Geek Culture
Published in
8 min readAug 18, 2021

DISCLAIMER: This tutorial requires Linux (and basic knowledge of it) on your PC where you host the server.

About one year ago, few of my friends and I have decided that we want to create a small Minecraft to play on during the quarantine. We put our money together and bought a server from a hosting. As the server grew, it became apparent that we needed more RAM and CPU cores, but the hosting was already a bit expensive. I could’ve moved our server to another hosting or just pay the more expensive tier, but I’ve decided that I want to try hosting the server on my own. That way I could allocate any amount of RAM that my computer can offer and use as many cores as I want. Also a few weeks ago, I learned that our ISP has doubled our upload speeds to 10Mbps which should be sufficient for a Minecraft server, but because we don’t have a public IP and are behind a CGNAT, I couldn’t just port forward from our router. Also a public IP is 25$ a month at my ISP. As soon as I found out that our upload speeds were doubled, I started researching on how I could make this work.

What will we need?
First, we’ll need a computer or a server that will host the Minecraft server. It has to have quite decent specs. I wouldn’t recommend PCs with less than 8GB of RAM or less than 2 cores. The higher the specs, the smoother is the server going to be.

Secondly, a stable network connection. You could make this work with an upload speed of 5Mbps but I would recommend at least 10Mbps.

Lastly, a computer which has a public IP. It can be a VPS server which may be the easiest solution for most.

If you don’t have a VPS but want to use one for this project, you can help me by using my referral link. For every user that signs up and adds at least 10$, I will get 10$ in credit.

I’m going to assume that we have an Ubuntu VPS set up and ready.

Setting up the Minecraft server

I’m going to use a Docker container for our Minecraft server. It creates and sets up everything for you. All of this stuff is going to happen on our PC which will be hosting the server, there’s no need for VPS yet.
Let’s start with installing Docker.

Update package index and install some prerequisities:
sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

Import the repository’s GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Add the repo to apt:
sudo add-apt-repository “deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable”
And lastly install the packages:
sudo apt install docker-ce docker-ce-cli

Next, we’ll create a container and use image from itzg which has everything set up for us:

sudo docker run -d -it --restart unless-stopped -p 25565:25565 -e MEMORY=4G -e EULA=TRUE -e TYPE=AIRPLANE -e AIRPLANE_BUILD=lastSuccessfulBuild -e USE_AIKAR_FLAGS=true -v /your/data/folder:/data --name minecraft itzg/minecraft-server

Some variables you can change:
MEMORY: Specify the amount of RAM you want to use for the server.
-v /your/data/folder:data: Change the “/your/data/folder” to the folder you want the server located in. For example /home/user/minecraft-server

Some explanations:
-e TYPE=AIRPLANE: We’re going to use a very optimized fork of Paper which itself is an optimized fork of Spigot.
-e USE_AIKAR_FLAGS=true: Enables some JVM flags which can help when more users are connected

The command will get everything ready for you.
You can see more info about the container using sudo docker ps.
Now if I want to go to the console, i can just do sudo docker attach minecraft. This will enable me to type directly into the console. To leave the console, DO NOT use CTRL+C. This would stop the server. Rather use an escape sequence. First press CTRL+P and then CTRL+Q.

To make it a bit more user friendly, I’m going to install portainer-ce. It’s an UI used for managing docker through a website.
To install portainer, use this command:

sudo docker run -d -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /your/data/folder:/data portainer/portainer-ce

You need to change the /your/data/folder to a folder where portainer related data will be stored.
Then go to localhost:9000, create an account and log in. We’re going to change how the networking works for our Minecraft server so click on “local”, then “Containers”, then “minecraft”. On the top click the duplicate/edit button. Scroll down to Advanced container settings and click on Network. Now change the network from bridge to host. This will disable the container isolation on the network and make it accessible for connections outside our LAN. Now click on “deploy the container”. If you want to look at the console from a graphical interface, you can just click on the logs to see the console output and attach to enter commands to the console.

We have our Minecraft server set up. You can connect to it from the multiplayer menu if you use localhost:25565 as the IP. Others in your LAN can connect using your local IP (use command ip a and find the interface you’re using) with the same port 25565. The next step is to setup our VPS and create a Wireguard VPN.

Setting up the VPS, Wireguard and the routing

I hope you can buy the VPS on your own. The lowest tier will do because running a VPN and routing a few packets doesn’t need that much resources. You should also choose a server that’s close to you, because if anyone wants to connect to the server, they’ll be connected to the VPS and the VPS will then connect to your PC. After you buy the VPS ssh into it.

First of all, we will enable packet routing. If you don’t have nano installed, install it using sudo apt install nano. Then run the following command: sudo nano /etc/sysctl.conf and uncomment the line with net.ipv4.ip_forward = 1. Exit using CTRL+X, then Y and finally Enter. This will enable ip forwarding from the VPS. Use this command to make the changes take effect without rebooting: sudo sysctl -p.

Let’s install ufw which stands for uncomplicated firewall by using sudo apt install ufw. Then lets allow ssh, wireguard and our minecraft ports.

sudo ufw allow ssh
sudo ufw allow 51820/udp
sudo ufw allow 25565
sudo ufw enable

If you notice that ufw isn’t working after reboot, try using sudo systemctl enable ufw.

We’re ready to set up Wireguard. First, lets install it by sudo apt install wireguard. Install it on your PC too. After you do that, we’ll need to create our keys. Run those two commands on both the VPS and the PC and save the public keys somewhere.

(umask 077 && printf "[Interface]\nPrivateKey = " | sudo tee /etc/wireguard/wg0.conf > /dev/null)
wg genkey | sudo tee -a /etc/wireguard/wg0.conf | wg pubkey | sudo tee /etc/wireguard/publickey

Now we need to change the Wireguards config on the VPS. First use sudo nano /etc/wireguard/wg0.conf. Then change your config like this:

[Interface]
PrivateKey = (this will be filled in by the previous command, do not share this with anyone)
PostUp = iptables -t nat -A PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.20.4.2:25565
PostUp = iptables -t nat -A POSTROUTING -o if -j MASQUERADE
PostDown = iptables -t nat -D PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.20.4.2:25565
PostDown = iptables -t nat -D POSTROUTING -o if -j MASQUERADE
ListenPort = 51820
Address = 10.20.4.1/24
[Peer]
PublicKey = (put here the public key from your PC that the previous command generated)
AllowedIPs = 10.20.4.2/24

Now you have to replace if with the interface that connects you to the Internet. You can check which interface is that with the route command. The interface you want is the default destination.

Output of the route command.

As you can see, in my case the interface would be enp0s3.

The PostUp and PostDown commands set up the routing so that all incoming traffic coming to port 25565 is redirected to our PC (10.20.4.2 in our VPN). The VPS’ IP is going to be 10.20.4.1 as you can see in the Address entry.

TIP: If you ever need to do port mapping or open another port, just copy the original iptables command and change the ips. The --dport is the port on the VPS and the PC’s port is in --to-destination part of the command. Don’t forget to enable the port with ufw. So as an example if I want to open the ssh port on my PC, I would add PostUp = iptables -t nat -A PREROUTING -p tcp --dport 555 -j DNAT --to-destination 10.20.4.2:22 and the PostDown command with -D instead of -A. Then enable the port with sudo ufw allow 555. You can now connect to your PC at home using ssh -p 555 (VPS’ public IP)@username.

Next we will set up the PC’s Wireguard.
Let’s open our config and change it a little bit. We can use sudo nano /etc/wireguard/wg0.conf again to change it. The config should look like this:

[Interface]
PrivateKey = (this will be filled, do not share this with anyone)
Address = 10.20.4.2/24
Table = 1
PostUp = ip rule add pref 500 from 10.20.4.2 lookup 1
PostDown = ip rule del pref 500
[Peer]
PublicKey = (put here the public key from the VPS)
AllowedIPs = 0.0.0.0/0
Endpoint = (VPS' public IP - the one used for ssh for example):51820
PersistentKeepalive = 25

It’s almost the same but we have set up routing to use a specific table. When you set AllowedIPs, Wireguard will set that your PC shouldn’t use the WAN when sending traffic to that IP but rather use the wg0 interface which is our VPN tunnel. We set the AllowedIPs to 0.0.0.0/0 to allow connections from all the IPs to be allowed through our VPN. If we didn’t do that and set it to the VPS’ IP, we would get the VPS’ IP in the logs and not the player’s (we would also have to change the VPS’ config a bit). However when we set it to 0.0.0.0/0, we tell Wireguard that all of the IPs should be routed through the wg0 interface. This makes it that all of the traffic will be routed through your VPN to the VPS and from there to the Internet. If you have limited monthly bandwidth on your VPS which most of them do, this may not be the thing you want. Most VPS providers’ lowest tier offers at least a 1 TB of monthly bandwidth. So If you want to route all the traffic through the VPN, remove the Table = 1 and both PostUp and PostDown. The ip rule add pref 500 from 10.20.4.2 lookup 1 makes sure that all of the traffic coming from 10.20.4.2 (your PC’s VPN adress) will use table “1” to route all the traffic. We use the PC’s VPN IP because we want to route the packets that our PC creates. This means that what comes out of the VPN goes back through the VPN and not the WAN.

Now all we need to do is just enable Wireguard with our config and everything should be working.

To enable Wireguard, enable it on both the VPS and your PC through systemctl using this command: sudo systemctl enable wg-quick@wg0
If you ever need to quickly restart the VPN, use sudo systemctl restart wg-quick@wg0
You should restart the VPS’ connection first and then the PC’s.
Also you can check that the connection is working by using sudo wg. If you’re getting handshakes, you’re connected through the VPN.

If this isn’t working for you, I’ll try to help you in the comments.

--

--