Running a home-server has a number of advantages over renting from a cloud provider. Over a surprisingly short period of time the costs of building a dedicated server are less than that of renting a similar computer in the cloud. Doing so also provides you with the flexibility to expand and upgrade your system as needed.

With cloud providers you are limited by the specifications of the VMs they offer. If you require more storage or processing power you may find yourself having to upgrade to a more expensive plan resulting in additional costs. With a home-server you have the freedom to add more storage, upgrade your CPU, etc.

One trouble with running a home-server is exposing the machine to the public Internet. Especially if you are operating online as a pseudonymous individual exposing sensitive information like your IP address from which your jurisdiction can be gleaned is a misstep. To prevent this one can rent a cloud VPS to act exclusively as an ingress controller for the home-server.

Basic Concept

We will create a Tailscale-like network to connect the home-server to the cloud VPS. This way both computers will be on the same subnet and can communicate as if they were on the same LAN.

The technology we'll use here:

  • Wireguard, for creating the network connecting the home-server cluster to the cloud VPS
  • Nginx, as the ingress controller for HTTP/S
  • iptables, for routing any non-HTTP/S traffic through the Wireguard tunnel

With this setup we can ensure secure communication between the home-server and the cloud VPS while also providing a secure and controlled ingress point for HTTP/S traffic.

You can skip the next section if you already have a home-server with Wireguard set up like I describe in Wireguard Point-to-Home-Server Setup.

Setting up Wireguard on the Home-Server

First we need to set up the Wireguard network between the home-server and the cloud VPS. On the home-server it is necessary to install Wireguard and generate a private key for the server. This can be done like:

[root@home-server ~]$ wg genkey | tee privatekey | wg pubkey > publickey

The privatekey file will contain the generated private key while the publickey file will contain the corresponding public key. Using this information we can configure wireguard on the home-server using this template:

[Interface]
PrivateKey = <home-server-private-key>
Address = 10.0.20.1/24
ListenPort = 51871

[Peer]
PublicKey = <cloud-vps-public-key>
AllowedIPs = 10.0.20.2/32

Note that ports on the home router may need to be forwarded for the server to know of any incoming Wireguard connections. Additionally the cloud-vps-public-key is generated below so fill that part out later. Save the file as wg0.conf in the /etc/wireguard/ directory on the home-server.

Setting up Wireguard on the Cloud VPS

Next we should install Wireguard on the cloud VPS and generate a private key for it as well. This is exactly the same as generating a public / private pair for the home-server.

[root@cloud-vps ~]$ wg genkey | tee privatekey | wg pubkey > publickey

On the VPS create a Wireguard configuration file using the following template:

[Interface]
PrivateKey = <cloud-vps-private-key>
Address = 10.0.20.2/24

[Peer]
PublicKey = <home-server-public-key>
AllowedIPs = 10.0.20.1/32, 10.0.10.0/24
Endpoint = <external-home-server-ip>:51871

Note that in addition to making 10.0.20.1 available I also expose 10.0.10.0/24 as that is the subnet of my On-Prem Home Lab Kubernetes pods.

Save the file as wg0.conf in the /etc/wireguard/ directory.

Starting the Wireguard Connection

Now that the Wireguard configuration files are in place we can start the connection on both the home-server and the cloud VPS.

On both computers run wg-quick up wg0 which should start the Wireguard connection and allow the computers to communicate.

To verify that the Wireguard connection is working properly use the wg command on both the home-server and the cloud VPS. Check connectivity using ping between both computers also.

Setting up Nginx as the Ingress Controller

With the networks bridged let's move on to actually forwarding traffic. For HTTP/S traffic my proxy of choice is nginx. An instance of nginx running on the cloud VPS will listen for connections on ports 80 and 443 then route traffic on those ports to our home server via the Wireguard interface we set up earlier.

A server block may look like this:

server {
    listen 80;
    listen [::]:80;
    server_name web.hooya.wesl.ee;

    location / {
        proxy_pass http://10.0.10.6:30005;
    }
}

This forwards traffic from my ingress cloud VPS to my local cluster. In addition to this I have these caching rules in place:

http {
    # ...
    proxy_cache_path /etc/nginx/cache-cid levels=1:2 keys_zone=cidcache:10m max_size=10g inactive=1y;
    proxy_cache_path /etc/nginx/cache-thumbs levels=1:2 keys_zone=thumbscache:10m max_size=10g inactive=1y;
}
server {
    # ...
    location /cid-thumbnail {
        proxy_cache thumbscache;
        add_header X-Cache-Status $upstream_cache_status;
        proxy_cache_valid 200 1y;
        proxy_pass http://10.0.10.6:30005;
    }
    location /cid-content {
        proxy_cache cidcache;
        add_header X-Cache-Status $upstream_cache_status;
        proxy_cache_valid 200 1y;
        proxy_pass http://10.0.10.6:30005;
    }
}

These rules are not necessary or even advised but as I know my content available on HooYa is immutable I can make cache guarantees like this. Caching on the VPS reduced the page load time of this page from 3.3s to 560ms because my home network upload speed is atrocious (about 40Mbps tonight).

Setting up iptables for Routing

Running nginx on the cloud VPS makes it simple to manage HTTP/S traffic. But what about other types of traffic? We can handle it with these commands, which enable IPv4 forwarding and set up the necessary iptables rules. In this example I forward only a port for a Garry's Mod server, but these commands could be run for any arbitrary port:

[root@cloud-vps ~]# echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
[root@cloud-vps ~]# sysctl -p
[root@cloud-vps ~]# iptables -A FORWARD -i wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT
[root@cloud-vps ~]# iptables -t nat -A PREROUTING -p udp --dport 27015 -j DNAT --to-destination 10.0.10.6:27015
[root@clous-vps ~]# iptables -t nat -A POSTROUTING -o wg0 -p tcp --dport 27015 -d 10.0.10.6 -j SNAT --to-source 10.0.20.2

A POSTROUTING rule is necessary instead of a MASQUERADE because traffic from the home-server is not routed back through our VPS by default. It took an embarassing amount of time for me to realize this was the issue when I was trying MASQUERADE commands.

Conclusion

This setup allows for secure communication between the home-server cluster and the cloud VPS, enabling seamless access to services and resources on the home-server from the address of the cloud VPS. Not limited to just HTTP/S traffic, this setup also permits file-sharing traffic and generally any Internat application, for example email or P2P filesharing.

After following these steps there is no need to expose any port on your home network other than the Wireguard port. This will only obscure your home address though; for simple applications this is probably enough to completely mask it. There are no guarantees that the application itself will not leak data some other way, but this at the very least sets up an ingress server so you don't go handing out your public IP every time someone wants to access a service running on your server.