VOOZH about

URL: https://mjg59.dreamwidth.org/72095.html

⇱ mjg59 | Locally hosting an internet-connected server


You're viewing 👁 [personal profile]
mjg59
's journal

Create a Dreamwidth Account  Learn More
Reload page in style:  site   light

Matthew Garrett

Locally hosting an internet-connected server

👁 [personal profile]
mjg59
I'm lucky enough to have a weird niche ISP available to me, so I'm paying $35 a month for around 600MBit symmetric data. Unfortunately they don't offer static IP addresses to residential customers, and nor do they allow multiple IP addresses per connection, and I'm the sort of person who'd like to run a bunch of stuff myself, so I've been looking for ways to manage this.

What I've ended up doing is renting a cheap VPS from a vendor that lets me add multiple IP addresses for minimal extra cost. The precise nature of the VPS isn't relevant - you just want a machine (it doesn't need much CPU, RAM, or storage) that has multiple world routeable IPv4 addresses associated with it and has no port blocks on incoming traffic. Ideally it's geographically local and peers with your ISP in order to reduce additional latency, but that's a nice to have rather than a requirement.

By setting that up you now have multiple real-world IP addresses that people can get to. How do we get them to the machine in your house you want to be accessible? First we need a connection between that machine and your VPS, and the easiest approach here is Wireguard. We only need a point-to-point link, nothing routable, and none of the IP addresses involved need to have anything to do with any of the rest of your network. So, on your local machine you want something like:

And on your VPS, something like:

The addresses here are (other than the VPS address) arbitrary - but they do need to be consistent, otherwise Wireguard is going to be unhappy and your packets will not have a fun time. Bring that interface up with wg-quick and make sure the devices can ping each other. Hurrah! That's the easy bit.

Now you want packets from the outside world to get to your internal machine. Let's say the external IP address you're going to use for that machine is and the wireguard address of your local system is . On the VPS, you're going to want to do:




Now, all incoming packets for will be rewritten to head towards instead (make sure you've set to 1 via !). Victory! Or is it? Well, no.

What we're doing here is rewriting the destination address of the packets so instead of heading to an address associated with the VPS, they're now going to head to your internal system over the Wireguard link. Which is then going to ignore them, because the statement in the config only allows packets coming from your VPS, and these packets still have their original source IP. We could rewrite the source IP to match the VPS IP, but then you'd have no idea where any of these packets were coming from, and that sucks. Let's do something better. On the local machine, in the peer, let's update to to permit packets form any source to appear over our Wireguard link. But if we bring the interface up now, it'll try to route all traffic over the Wireguard link, which isn't what we want. So we'll add to the stanza of the config to disable that, and now we can bring the interface up without breaking everything but still allowing packets to reach us. However, we do still need to tell the kernel how to reach the remote VPN endpoint, which we can do with . Add this to the stanza as:

That's half the battle. The problem is that they're going to show up there with the source address still set to the original source IP, and your internal system is (because Linux) going to notice it has the ability to just send replies to the outside world via your ISP rather than via Wireguard and nothing is going to work. Thanks, Linux. Thinux.

But there's a way to solve this - policy routing. Linux allows you to have multiple separate routing tables, and define policy that controls which routing table will be used for a given packet. First, let's define a new table reference. On the local machine, edit and add a new entry that's something like:

where "1" is just a standin for a number not otherwise used there. Now edit your wireguard config and replace with - Wireguard will now update the routing table rather than the global one. Now all we need to do is to tell the kernel to push packets into the appropriate routing table - we can do that with , which tells the kernel to take any packet coming from our Wireguard address and push it via the Wireguard routing table. Add that to your Wireguard interface config as:
and now your local system is effectively on the internet.

You can do this for multiple systems - just configure additional Wireguard interfaces on the VPS and make sure they're all listening on different ports. If your local IP changes then your local machines will end up reconnecting to the VPS, but to the outside world their accessible IP address will remain the same. It's like having a real IP without the pain of convincing your ISP to give it to you.

no subject

Date: 2025-06-17 08:44 am (UTC)
From: 👁 [personal profile]
andrewducker
I'm curious whether your ISP provides IPv6, and whether that works without needing the VPS.

no subject

Date: 2025-06-17 09:27 am (UTC)
From: 👁 [personal profile]
mjg59
Yes, as long as I'm good with the range changing every 48 hours. Which I'm not, so no.

no subject

Date: 2025-06-17 09:35 am (UTC)
From: 👁 [personal profile]
andrewducker
Ah, so no static v6 either? That's frustrating!

Tailscale

Date: 2025-06-17 10:02 pm (UTC)
From: 👁 [personal profile]
pheedrus
Why not just use Tailscale?

Re: Tailscale

Date: 2025-06-17 11:03 pm (UTC)
From: 👁 [personal profile]
mjg59
I want to expose the entire machine, so Funnel doesn't do what I want here.

rke2 issues

Date: 2025-09-29 09:05 pm (UTC)
From: (Anonymous)
Thanks for the guide.
I had some issues doing this on a single-node rke2 cluster:
1. packets sent to pods completely ignored the ip rules
2. the ip rules got flushed every couple minutes
I was able to fix the first one by adding explicit rules for the ports in use:
ip rule add sport 30000-32767 lookup wireguard
ip rule add sport 80 lookup wireguard
ip rule add sport 443 lookup wireguard

and the second on by setting the table id to 1337 instead of 1. I guess the default CNI also uses id 1 for its routing table or something like that? No idea.
Hopefully this is helpful to anyone running into similar issues. :)

Small request

Date: 2026-02-12 11:39 pm (UTC)
From: (Anonymous)
Could you post the completed configuration of both wireguard files

Profile

Matthew Garrett

About Matthew

Power management, mobile and firmware developer on Linux. Security developer at nvidia. Ex-biologist. Content here should not be interpreted as the opinion of my employer. Also on Mastodon and Bluesky.

Expand Cut Tags

No cut tags
Top of page