Setting up WireGuard on a VPS

January 24, 2026

What is WireGuard?

WireGuard is a communication protocol and free and open-source software that implements encrypted virtual private networks (VPNs). The WireGuard protocol passes traffic over UDP.1

That is the Wikipedia definition which slightly creates an image of what WireGuard is.

In VPS setups, we usually run multiple services which listen to several ports on the host. Each exposed port to the public network (the Internet) expand our attack surface therefore each service requires care and attention for their security.

In a corporate setup, these issues are usually taken care of by cyber-security, networking professionals and system admins. But in a homelab environment where you just want to self-host your own services, where you create Docker containers and expose them to the network, it is not usually the case.

On another hand, what if you (with your laptop) and your server are not in the same local area network? Router is pretty far away to route your packets to your server’s local IP which is written on a tiny piece of paper.

  • Maybe you can write your router’s public IP to the same paper before leaving home, then port forward to your homelab?
    • It is very likely that it will be changed until you sit in a cafe.
  • What if I buy static IP from my internet service provider?
    • You don’t really want to expose the same router to the internet which your mama and papa watch their favourite show in the evening.

I don’t want to yap too much; you just create a VPN in a VPS (with public IP) and connect clients. WireGuard is a good option due to:

  • Simpler to setup and configure
  • Encrypted connection
  • Minimized attack surface

VPN


Setting up the VPS side

Install required packages

Install the wireguard and ufw (uncomplicated firewall) packages from your distribution’s repositories.

  • For Fedora/RHEL based distros:
~$ dnf install wireguard ufw
  • For Debian/Ubuntu based distros:
~$ apt install wireguard ufw

Enable IP forwarding

Add these lines to /etc/sysctl.conf (kernel parameters file):

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Run “sysctl -p” to load in sysctl settings.


Creating keys and configuration file

Create configuration directory and change to that directory:

~$ mkdir /etc/wireguard && cd /etc/wireguard

Generate server private key of server:

~$ wg genkey | tee /etc/wireguard/server_private.key

Change private key file permissions (only owner/root can read+write):

~$ chmod 600 /etc/wireguard/server_private.key

IMPORTANT: Setting restrictive file permissions to private keys in general is really crucial especially in multi-user environments! And NEVER SHARE THE PRIVATE KEY!

Generate public key of server:

~$ cat /etc/wireguard/server_private.key | wg pubkey | tee /etc/wireguard/server_public.key

Create the configuration file at “/etc/wireguard/wg0.conf”:

[Interface]
Address = {SERVER_VPN_IP}/{SUBNET_PREFIX} # Example: 130.10.0.1/24
ListenPort = {SERVER_PORT}
PrivateKey = {SERVER_PRIVATE_KEY}

PostUp = ufw route allow in on wg0 out on {SERVER_NIC}
PostUp = iptables -t nat -I POSTROUTING -o {SERVER_NIC} -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o {SERVER_NIC} -j MASQUERADE
PostDown = ufw route delete allow in on wg0 out on {SERVER_NIC}
PostDown = iptables -t nat -D POSTROUTING -o {SERVER_NIC} -j MASQUERADE
PostDown = ip6tables -t nat -D POSTROUTING -o {SERVER_NIC} -j MASQUERADE

Replace SERVER_VPN_IP, SUBNET, SERVER_PORT, SERVER_PRIVATE_KEY and SERVER_NIC with your own values (delete curly braces):

  • SERVER_VPN_IP: IP address of your server in virtual network
  • SUBNET_PREFIX: Subnet prefix of virtual network
  • SERVER_PORT: Port number which WireGuard will listen on. 51820 is usually prefered but since it’s usually prefered, I recommend not to use it.
  • SERVER_PRIVATE_KEY: The key which we just generated at “/etc/wireguard/server_private.key
  • SERVER_NIC: Network interface which has the public IP. You can run “ip a” to find out (it’s usually eth0).

Change configuration file permissions since it includes server private key:

~$ chmod 600 /etc/wireguard/wg0.conf

Configure and start ufw (uncomplicated firewall)

Run following:

~$ ufw default deny incoming
~$ ufw default allow outgoing
~$ ufw allow {WIREGUARD_PORT}/udp
~$ ufw allow {SSH_PORT}/tcp
~$ ufw enable

Replace SSH_PORT and WIREGUARD_PORT(as specified in config file) with your own values (delete curly braces).

Here, it is assumed that there are no listening ports other than SSH and WireGuard. If you already have some services that needs to listen other specific ports, allow them.

Setting up the client side

Install wireguard to client

Install the wireguard package from your distribution’s repositories.

  • For Fedora/RHEL based distros:
~$ dnf install wireguard
  • For Debian/Ubuntu based distros:
~$ apt install wireguard

Creating keys and configuration file

Generate private-public keys for the client and change file permissions(as we generated in the server):

~$ mkdir /etc/wireguard && cd /etc/wireguard
~$ wg genkey | tee /etc/wireguard/client_private.key
~$ cat /etc/wireguard/client_private.key | wg pubkey | tee /etc/wireguard/client_public.key
~$ chmod 600 /etc/wireguard/client_private.key

Create the configuration file at “/etc/wireguard/wg-client0.conf”:

[Interface]
Address = {CLIENT_VPN_IP}/{SUBNET_PREFIX}  #Example: 130.10.0.2/24
PrivateKey = {CLIENT_PRIVATE_KEY}

[Peer]
PublicKey = {SERVER_PUBLIC_KEY}
AllowedIPs = {NETWORK_ADDRESS}/{SUBNET_PREFIX}  #Example: 130.10.0.0/24
Endpoint = {SERVER_PUBLIC_IP}:{SERVER_PORT}
PersistentKeepalive = 25

Change client directory permissions (only owner/root can read+write+execute):

~$ chmod 700 /etc/wireguard

Add client to server config as peer

Add following to server configuration for each client:

[Peer]
PublicKey = {CLIENT_PUBLIC_KEY}
AllowedIPs = {CLIENT_VPN_IP}/{ALLOWED_SUBNET_PREFIX}  #Example: 130.10.0.2/32

Enable and restart Systemd services for WireGuard

On server:

~$ systemctl enable wg-quick@wg0
~$ systemctl restart wg-quick@wg0

On client:

~$ systemctl enable wg-quick@wg-client0
~$ systemctl restart wg-quick@wg-client0

Test connection

  • wg show to print current state of WireGuard on both client and server

On server:

  • systemctl status wg-quick@wg0.service
  • ping {CLIENT_VPN_IP}

On client:

  • systemctl status wg-quick@wg-client0.service
  • ping {SERVER_VPN_IP}

wireshark capture