Using Wireguard to Self-Host Around a Carrier-Grade NAT

Here’s another log that’s half notes for myself and half information for anyone who needs it.


I first took a look at Nextcloud a few months ago and decided it was a little much for what I needed. But needs change and since early last month I’ve been self-hosting my own Nextcloud instance on an old office computer.

Making my Nextcloud available from outside my network was less than straightforward due to the situation with my apartment internet. Basically, there’s another device between my router and the internet doing network address translation. This makes it either impossible or at least a big hassle for me to get my own public IP.

The way I chose to work around this was by setting up a VPS with a provider I’ve used before (DigitalOcean), configuring a Wireguard VPN server on it, and connecting to it from my machine hosting Nextcloud. My Wireguard server is configured to route all incoming web traffic to my Nextcloud server and my Nextcloud server is configured to route all outgoing traffic through my Wireguard server. The end result is that I’m now able to talk to my VPS and it’s as if I’m talking to my Nextcloud server.

Getting Started

There are a few guides on how to do this out there already, but there’s a few things I didn’t like about each one I looked at. I borrowed from a few different ones to come up with my current config which I’ve written about here.

The instructions below were written with Ubuntu in mind but they should be pretty general and work on most other Linux flavors. A lot of what’s in this guide will also need sudo privileges.

First thing you’ll want to do is of course get a VPS started. DigitalOcean and Linode are both popular providers that offer fairly cheap basic plans, though for this purpose you might be able to find an even cheaper option elsewhere. From now on I’m going to be referring to the VPS as the server and the machine you’re self-hosting from as the client.

Next you’ll want to install Wireguard, which most likely should be in your OS’s package repositories. Go ahead and do this on both the server and the client.

Generate Keys

Navigate to /etc/wireguard (or any other directory you would like to save your keys in) and run the following commands on both the server and the client to generate your public and private keys.

wg genkey | tee privatekey | wg pubkey > publickey

Server Configuration

Here’s my server config which I’ve saved as /etc/wireguard/wg0.conf.

PrivateKey = <server's private key>
Address =
ListenPort = <port you want to listen on>

PreUp = iptables -t nat -A PREROUTING -d <server's public ip> -p tcp --dport 80 -j DNAT --to-destination
PreUp = iptables -t nat -A PREROUTING -d <server's public ip> -p tcp --dport 443 -j DNAT --to-destination
PreUp = iptables -t nat -A POSTROUTING -o <your network interface, ex. eth0> -j MASQUERADE
PostDown = iptables -t nat -D PREROUTING -d <server's public ip> -p tcp --dport 80 -j DNAT --to-destination
PostDown = iptables -t nat -D PREROUTING -d <server's public ip> -p tcp --dport 443 -j DNAT --to-destination
PostDown = iptables -t nat -D POSTROUTING -o <your network interface, ex. eth0> -j MASQUERADE

PublicKey = <client's public key>
AllowedIPs =

Notice the PreUp and PostDown rules. These modify iptable rules when the wireguard connection goes up or down in order to forward incoming traffic to the client. Some guides I’ve seen online have these rules set up to forward ALL incoming traffic to the client, but I would strongly discourage that. Instead, you can use the rules that I have in this config to forward only the ports you intend to use. I have rules for ports 80 and 443 for http and https. You can modify these rules, remove them, or add more for your needs.

Next you’ll need to enable IP forwarding with the following command. You may also need to edit /etc/sysctl.conf to make the setting persistent.

sysctl net.ipv4.ip_forward=1

Client Configuration

The config file for the client is a bit simpler than the server. Save it as /etc/wireguard/wg0.conf.

PrivateKey = <client's private key>
Address =

PublicKey = <server's public key>
Endpoint = <server's ip address>:<ListenPort from server config>
AllowedIPs =
PersistentKeepAlive = 25  # necessary to keep the connection alive

You’ll need to add two iptable rules. Run the following commands and also add them to /etc/rc.local to make them permanent across reboots.

iptables -A FORWARD -i wg0 -j ACCEPT
iptables -t nat -A POSTROUTING -o <your network interface, ex. eth0> -j MASQUERADE

Like the server, you’ll also need to enable IP forwarding. Run the following command to do that. You might also need to edit your /etc/sysctl.conf to make it persist after a reboot.

sysctl net.ipv4.ip_forward=1

Testing It All Out

You can enable, start, and check the status of Wireguard with these commands on both the client and the server:

systemctl enable --now wg-quick@wg0
systemctl status wg-quick@wg0
wg show

If everything’s worked out you should be able to tell that the connection is active when you see a “latest handshake” and “transfer” in the output for “wg show”.