General

How to Set Up a WireGuard VPN on Your Server

June 29, 2026
10 min read

A practical WireGuard tutorial for Ubuntu: install it, generate keys, write the server config, enable NAT, add a client, and verify the tunnel works.

How to Set Up a WireGuard VPN on Your Server | Carpathian

You can set up a WireGuard VPN on an Ubuntu server in well under half an hour, and most of that is reading. The short version: install the wireguard package, generate a key pair for the server and one for each device, write a small config file, turn on IP forwarding and NAT, then bring the tunnel up with wg-quick. WireGuard is a modern VPN protocol built into the Linux kernel, and its whole appeal is that it does this with a fraction of the code and config of older options like OpenVPN or IPsec.

This guide walks the full path on Ubuntu: server keys, client keys, the server config, forwarding and NAT, adding a phone or laptop, bringing the tunnel up, and checking that it works. If you are still choosing where to run it, see how to choose a VPS and which hosting type fits a small project. Before you expose anything to the internet, it is worth working through Ubuntu server hardening first.

What is WireGuard and why is it simple?

WireGuard is a VPN (virtual private network) that builds an encrypted tunnel between your devices and a server you control. It ships as a kernel module in modern Linux, so it runs in the kernel rather than as a userspace daemon, which makes it fast and light. The original code base is around 4,000 lines, compared with the hundreds of thousands in OpenVPN and IPsec, which is part of why it is easier to audit and to configure (WireGuard whitepaper, Jason A. Donenfeld).

The model is deliberately small. Every machine in the network is a "peer," and each peer has one private key and one public key. A peer accepts traffic from another peer only if it recognizes that peer's public key, so configuration is mostly a matter of exchanging public keys and listing which IP ranges each peer is allowed to use. There are no certificates to manage and no chatty handshakes. When no data is flowing, WireGuard is silent, which also makes it harder to detect.

One honest caveat before you start: a VPN you run yourself protects your own access and privacy, it is not a commercial no-logs service. Traffic leaving your server still carries your server's IP, and you are the one who can see the connection logs. That is the right tool for reaching your home or work network securely, or for browsing through a machine you trust. It is the wrong tool if your goal is anonymity from the people who run the server, because that person is you.

What you need before you start

Before the first command, make sure you have a server you can administer and a way to reach the right ports. WireGuard is undemanding, so almost any small instance handles a handful of devices without noticing. The prerequisites are short.

  • An Ubuntu server (22.04 or 24.04 work the same way here) with sudo access.
  • The server's public IP address or a domain name that points to it.
  • A free UDP port for WireGuard. The convention is 51820, and you will need to allow it through any firewall, including your cloud provider's security group.
  • Root or sudo on each client device you want to connect.

All server commands below assume you are running as a user with sudo. Run them on the server unless a step says it is for the client.

How do you install WireGuard on Ubuntu?

Installing WireGuard on Ubuntu is one package. The wireguard package pulls in the kernel module and the wg and wg-quick command-line tools, which are everything you need to generate keys, write configs, and control tunnels. On a current Ubuntu release the module is already part of the kernel, so there is nothing to compile.

sudo apt update
sudo apt install wireguard

That installs wg (the low-level tool) and wg-quick (a wrapper that reads a config file and sets up the interface, routes, and firewall rules for you). You will use wg-quick to start and stop tunnels and wg to inspect them.

How do you generate the server and client keys?

WireGuard authenticates peers with public-key cryptography, so the next step is generating a key pair for the server and one for each client. The wg genkey command makes a private key, and piping it through wg pubkey derives the matching public key. Keep private keys secret and share only public keys.

Set a tight umask first so the key files are created with locked-down permissions, then generate the server pair in /etc/wireguard:

umask 077
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key

That single line writes the private key to server_private.key and the derived public key to server_public.key. The umask 077 ensures both files are readable only by their owner, which matters because anyone who reads the private key can impersonate your server.

Generate a key pair for your first client the same way. You can do this on the server for convenience, then move the client config to the device later:

wg genkey | tee client_private.key | wg pubkey | tee client_public.key

Print the keys when you need to paste them into a config:

sudo cat /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_public.key
cat client_private.key
cat client_public.key

How do you write the server config and enable NAT?

The server config lives at /etc/wireguard/wg0.conf and defines the tunnel's interface plus one block per client. This file holds the server's private key, the internal subnet for the VPN, the listening port, and the firewall rules that let client traffic reach the internet. The [Interface] section is the server itself, and each [Peer] section is a client.

Create /etc/wireguard/wg0.conf with your editor and use this as the template:

[Interface]
PrivateKey = <server_private_key>
Address = 10.8.0.1/24
ListenPort = 51820
PostUp = iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.8.0.2/32

A few notes on the fields. Address = 10.8.0.1/24 gives the server the first address in a private VPN subnet that nothing else on your network uses. ListenPort = 51820 is the UDP port clients connect to. The PostUp and PostDown lines add and remove a NAT masquerade rule when the tunnel comes up and goes down, which is what lets a client route its internet traffic out through the server. Replace eth0 with your server's public interface, which you can find by running ip route get 1.1.1.1 and reading the dev name. In the [Peer] block, AllowedIPs = 10.8.0.2/32 restricts that client to a single address inside the tunnel.

NAT only works if the kernel is allowed to forward packets between interfaces, and that is off by default. Turn on IP forwarding permanently by adding a line to a sysctl drop-in, then apply it:

echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

If you also route IPv6 traffic, add net.ipv6.conf.all.forwarding = 1 to the same file and match it with an ip6tables masquerade rule and an IPv6 address on the interface.

How do you add a client, including a QR code for mobile?

A client needs its own config that names the server as a peer. The client config carries the client's private key, the address it takes inside the tunnel, the server's public key, the server's public address and port, and the range of traffic to send through the tunnel. For a phone, you can turn that config into a QR code and scan it instead of typing anything.

Create a client.conf file (on whichever machine will hold it) like this:

[Interface]
PrivateKey = <client_private_key>
Address = 10.8.0.2/24

[Peer]
PublicKey = <server_public_key>
Endpoint = your.server.ip.or.domain:51820
AllowedIPs = 0.0.0.0/0

Here Address = 10.8.0.2/24 matches the /32 you allowed for this peer on the server. Endpoint is the server's public IP or domain plus the listen port. AllowedIPs = 0.0.0.0/0 routes all the client's traffic through the tunnel, which is what you want for full-tunnel privacy. To use the VPN only for the private subnet and leave normal browsing alone, set AllowedIPs = 10.8.0.0/24 instead.

On a desktop, copy client.conf to /etc/wireguard/ on the client and start it with wg-quick. For the official WireGuard app on iOS, Android, or macOS, generate a QR code from the config and scan it. Install qrencode and render the config straight to the terminal:

sudo apt install qrencode
qrencode -t ansiutf8 < client.conf

In the WireGuard app, tap Add tunnel, then Create from QR code, and scan it (nixCraft, generate WireGuard QR codes). One warning that is easy to overlook: that QR code contains the client's private key, so anyone who scans or photographs it can connect as that device. Do not paste it into a chat or leave it on screen.

Managing these config files by hand gets tedious once you have more than a couple of devices. If you want a lighter way to keep client configs organized, the open-source carpathian-wg-vpn utility is a free cross-platform tool that discovers and connects WireGuard configs from a terminal UI, which is handy on systems that cannot run the official app.

How do you bring the tunnel up and verify it works?

Once both configs are in place, start the tunnel on the server and confirm the handshake from the client. The wg-quick command reads wg0.conf, creates the interface, applies the routes and the NAT rules, and brings it all up in one step. The wg show command then tells you whether peers have connected.

Start the tunnel on the server and enable it to come back after a reboot:

sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0

Check the interface and peers:

sudo wg show

A working tunnel shows each peer with a latest handshake time and rising transfer counters once data moves. Bring the client up the same way (sudo wg-quick up wg0 on a desktop, or toggle the tunnel on in the mobile app), then confirm traffic is flowing. From the client, ping the server's tunnel address and check your public IP:

ping 10.8.0.1
curl ifconfig.me

If the ping replies and curl returns the server's public IP rather than your local one, full-tunnel routing is working. To stop a tunnel, run sudo wg-quick down wg0.

Firewall and security notes

A VPN endpoint is exposed to the internet, so the firewall is part of the setup, not an afterthought. WireGuard listens on a single UDP port, which keeps the surface small, but you still need to allow that port and protect SSH. If you use ufw, allow the WireGuard port and OpenSSH, then enable it:

sudo ufw allow 51820/udp
sudo ufw allow OpenSSH
sudo ufw enable

A few habits keep a self-run VPN healthy. Allow only the UDP port WireGuard needs and nothing extra, since an unused open port is a liability. Generate a separate key pair for every device so you can revoke one without touching the rest, which is as simple as deleting that peer's block and reloading the tunnel. Keep the server patched, because WireGuard rides on the kernel and your firewall, and both need updates. None of this is exotic, it is the same baseline that applies to any internet-facing box.

A clean, small VPN you control

WireGuard rewards the same instinct that good infrastructure does: do less, and do it where it is cheap. A few dozen lines of config give you an encrypted path to your own network, fast enough that you forget it is there and small enough that you can read every rule. Keep the key files locked down, give each device its own keys, and the whole thing stays easy to reason about. The best infrastructure tends to be the kind you stop thinking about: efficient, quietly dependable, and no larger than the job in front of it.

About the Author

Samuel Malkasian | Founder

Samuel Malkasian | Founder

Samuel Malkasian is the founder and lead cloud architect at Carpathian, where he designed the platform's core architecture along with a range of client enterprise systems and open-source tools for AI workflows and integration. He serves as a Cyber Warfare Officer in the U.S. Army and has a background in machine learning and data science. He is currently focused on building AI infrastructure that is secure, efficient, and low-power by design.

Related Topics

wireguardvpnubuntuself-hostingnetworkingserver security