↓Skip to main content
  1. Posts/

Intro: A Tale of 2 Servers, UDP Ports, and K3s

I needed one server and ended up with two because of a blocked UDP connection. Here is why I joined them into one cluster and chose K3s over full Kubernetes.

I. A Favor and a Blocked Connection #

I needed one server and ended up with two because of a blocked UDP connection. Here is why I joined them into one cluster and chose K3s over full Kubernetes.

I needed one server for one web app. I ended up with two, and had to learn a new tool called K3s just to keep them both busy.

My web project had to go live fast. Instead of renting a VPS as usual, I asked a senior friend in the industry for a spare server. A VPS is a virtual server rented from a cloud provider. He sent me one right away, no hesitation.

During setup, I found the network provider blocked all UDP on it, in and out. My project’s architecture required exactly that protocol.

Quick primer: networks move data in two common ways, like two ways a restaurant handles delivery.

  • TCP calls to confirm the order first, resends any missing dish, and delivers everything complete and in order. Reliable, but slower.
  • UDP delivers the moment the order arrives, no call; a dish dropped on the way stays dropped. It is fast, so most modern VPNs run on it.

A VPN is an encrypted private network over the internet, letting distant machines talk as if they sat in one office.

That missing call is also why some providers block UDP. An attacker sends small requests to a server with the victim’s address faked as sender, and the server replies to the victim, sometimes with a reply many times larger. Like ordering heaps of food to a stranger’s address: it takes seconds, and the food piles up at their door. Across many servers, that is a DDoS, a traffic flood that swamps a system until it stops serving. With TCP, the restaurant calls first and the victim never ordered, so the trick rarely works.

The block sat in the network, not the server, so no server setting could lift it. My friend filed a ticket asking the provider to open the port, and we waited. A port is a numbered door on a machine; each service uses its own.

II. How One Server Became Two #

The project could not wait. I shrugged and bought a brand-new server from another provider with my own money.

Next morning, support closed the ticket: they could only open UDP for domestic IP addresses. UDP going abroad stayed fully blocked.

That was when I learned his server was no old spare. He had quietly bought it new, with his own money, to rent to me at no extra charge so I could hit my deadline. In short, I had just bought a new server to replace a brand-new one bought specifically for me.

I could not turn down that generosity, so I kept it. Now I had 2 servers for one web app.

Letting one machine do all the work while the more powerful one sat idle was a waste. So the idea came: “Why not join the two into a cluster that shares the load and resources, with each backing up the other?” A cluster is a group of machines that software runs as one system.

That idea became this series. But a cluster needs software to control it, and my first pick was heavier than I expected.

III. Kubernetes Was Too Heavy, K3s Fit #

I reached for Kubernetes, the industry’s most widely used standard for this job. On day one, I learned “the industry standard” is cut for clusters far bigger than my two servers.

Kubernetes is software that coordinates apps across many machines: it decides what runs where and brings apps back when a machine dies. Its weight comes from the control plane, the cluster’s brain. The control plane remembers the desired state, say “2 copies of the web app always running”, and keeps adjusting the system to match. It runs nonstop, and on my mid-sized VPS it ate nearly all the RAM and CPU, leaving little for the app.

The way out was K3s, a slimmed-down Kubernetes. It drops rarely used old features and the add-ons for specific big cloud providers, then packs the rest into one program file. The core stays intact, so standard Kubernetes tools still work.

It also moved me from Docker, my tool for running apps one server at a time, to Kubernetes. Paper 01 covers why Docker falls short for a cluster. Meanwhile, UDP going abroad was still blocked.

IV. The Final Setup: A Private Network, a Relay, and a Home PC #

The problem began as one server that could not send UDP abroad. The final setup has a whole private network, a relay server, and my home PC:

  • Tailscale as the core network. With UDP abroad blocked, the VPN between machines kept dropping and fell back to the much slower TCP. So I chose Tailscale, a VPN service that links machines directly wherever they are. It finds its own way through routers and firewalls, which usually block incoming connections.
  • My own DERP server on the K3s cluster. DERP is Tailscale’s relay server: when two machines cannot connect directly, data detours through it. It carries data over HTTPS, the protocol browsers use to open websites, which runs on TCP, so it gets through when UDP is blocked. Running my own keeps the detour on my machine rather than Tailscale’s distant shared relays, cutting delay.
  • My home PC in the cluster for AI work. Once the network worked, I connected my home PC directly to the cluster. A GPU is a graphics chip, also the chip AI workloads usually need; using mine at home saves a lot versus renting one.

The result is a hybrid cloud cluster. Hybrid cloud mixes machines rented from the cloud with hardware you own, here a VPS and my home PC.

It was also my first real hands-on Kubernetes work on a personal project. So this series is both an architecture log and a from-scratch K3s guide for anyone who ends up with 2 servers, by accident or on purpose.

Renting or borrowing a server for an app that needs UDP, like a VPN or video calls? Ask the provider first whether UDP is blocked, including traffic abroad. Paper 01 starts where Docker stops being enough.