Building a Private Cluster on Hetzner (Part 2) - Air-Gapping with a Private VPN
Once I had my Hetzner bare metal server up and running, I wanted to isolate it from the public internet without losing remote access. The answer: a private VPN endpoint that acts as a controlled entry point into the cluster.
This post covers the setup in detail using Pritunl and both WireGuard and OpenVPN (TCP) as VPN protocols.
Why a Private VPN?
The moment a server is exposed to the internet, it becomes a target. Port scans, SSH brute force attempts, and other forms of probing happen within minutes.
Instead of relying on IP whitelisting or hoping SSH holds up under attack, I decided to:
Completely block SSH from the public internet
Funnel all admin access through a secure VPN
Treat the VPN as the only entry point into the private cluster
This effectively air-gaps the node, while I still retain complete control.
Pritunl vs. OpenVPN OSS
I considered setting up OpenVPN manually, but Pritunl turned out to be a better choice for my goals.
OpenVPN (OSS):
Fully CLI driven
Flexible but complex
Requires manual management of certs, keys, users, and routes
Pritunl:
Clean web interface
Built-in support for both WireGuard and OpenVPN
Handles user and certificate management
Easy to scale if I add more nodes later
For a single-node private cluster, the free version of Pritunl was more than sufficient.
Protocol Strategy: WireGuard + OpenVPN TCP
I chose to enable both WireGuard and OpenVPN over TCP for flexibility.
Why both?
WireGuard: Fast, modern, and simple. Great for low-latency access from personal devices.
OpenVPN over TCP: More resilient in restrictive environments (hotels, corporate networks) that block UDP.
Port Security Tip:
Both protocols use well-known default ports:
WireGuard default: 51820/udp
OpenVPN default: 1194/tcp
I changed both to non-standard high ports to reduce noise from bots and scanners:
WireGuard: 51234/udp
OpenVPN TCP: 14444/tcp
This helps minimise automated scans and random login attempts.
Installing Pritunl on Ubuntu 24.04
I followed the official instructions to install Pritunl and MongoDB
# Add repository
echo "deb https://repo.pritunl.com/stable/apt jammy main" | sudo tee /etc/apt/sources.list.d/pritunl.list
sudo apt install gnupg
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com --recv-keys 7568D9BB55FF9E5287D586017AE645C0CF8E292A
# Install packages
sudo apt update
sudo apt install pritunl mongodb-org -y
# Start services
sudo systemctl enable mongod pritunl
sudo systemctl start mongod pritunl
After installation, the web dashboard was available at https://<your-server-public-ip>.
Changing Pritunl Dashboard Port
To avoid default port-based attacks, I changed the Pritunl dashboard port from 443 to something like 14443.
This can be done in the dashboard under Settings > Port.
Configuring Pritunl
Here’s how I set it up after logging in using the setup key:
sudo pritunl setup-key
Configuration steps:
Change admin password on first login
Create an Organization
Create a Server by enabling
WireGuard (port 51234/udp)
OpenVPN TCP (port 14444/tcp)
Add Users under the organization
Configure Routes as required
Important: Remove 0.0.0.0/0 Route
By default, Pritunl adds a full-tunnel route (0.0.0.0/0) which sends all your internet traffic through the VPN. I deleted this to avoid:
Slowdowns in general internet browsing
DNS leaks and latency issues
Wasting Hetzner bandwidth
I only kept routes to the internal VPN subnet, like 192.168.100.0/24 (check your internal VPN subnet in Pritunl Dashboard under Servers, this is only an example)
Hetzner Firewall Configuration
Hetzner’s robot stateless firewall is easy to use and quite effective. I used it to block everything except:
WireGuard port: 51234/udp
OpenVPN TCP port: 14444/tcp
Pritunl dashboard port: 14443/tcp (allowed only from my ISP IP)
I completely blocked port 22 to enforce VPN-only SSH access.
Testing Access
After configuring and downloading the VPN profiles, I connected and tested SSH:
ssh root@192.168.100.1 #Use your VPN Server subnet first IP
Both protocols worked as expected. I now access the server only via VPN, with no open public ports other than the ones explicitly allowed.
What’s Next
Now that the server is secure and accessible only through private VPN tunnels, I’ll move on to setting up a K3s Kubernetes cluster.
In Part 3, I’ll walk through installing K3s, initialising the cluster, deploying workloads, and integrating it with monitoring and CI/CD tools.
Stay tuned.
References
Pritunl Documentation: https://docs.pritunl.com
Hetzner Firewalls: https://docs.hetzner.com/robot/dedicated-server/firewall/
WireGuard Overview: https://www.wireguard.com/
OpenVPN Protocol Info: https://openvpn.net/