How to Set Up WireGuard on a Linux VPS

Running your own VPN server used to mean wrestling with OpenVPN's certificate authority workflow or IPsec's famously fiddly configuration stanzas. WireGuard was built to sidestep both problems: it's a VPN protocol implemented as a small codebase that the Linux kernel has shipped as a built-in module since late 2019, according to the Arch Linux wiki's WireGuard page. This guide sets one up end to end on a Linux VPS — installing the tool, generating keys, writing the configuration file, and bringing the tunnel up — using the commands WireGuard's own documentation and manual page specify.

Quick Verdict
WireGuard installs as a single package on every major Linux distribution, needs only a key pair per peer and a short INI-style config file to establish a tunnel, and skips the certificate authority and TLS handshake overhead that older VPN protocols carry. The trade-off is that you're the one managing keys, config files, and firewall rules — there's no vendor dashboard behind it. If you want a VPN endpoint you fully control on a VPS you already pay for, this is a same-afternoon project.

What WireGuard Actually Is

WireGuard is a VPN protocol and set of tools designed around a deliberately small, auditable implementation rather than the sprawling option lists of OpenVPN or IPsec. On Linux it runs as a kernel module — merged into the mainline kernel in late 2019, per the Arch Linux wiki — which is why a modern distribution needs no external driver or DKMS build to use it in the common case. Each connection is defined by a pair of public/private keys per device rather than a certificate chain, and the protocol only exchanges traffic when a peer actually has something to send, which keeps it quiet on the wire compared to protocols that maintain a constant keep-alive handshake.

Two pieces of software make up a working setup: the kernel (or userspace) implementation that actually moves packets, and wg, the command-line configuration utility documented in the wg(8) manual page. Everything below builds on those two pieces directly.

Installing WireGuard on Your VPS

Per WireGuard's official installation page, the package name and manager differ slightly by distribution, but the install itself is a single command on every major one:

 1# Ubuntu
 2sudo apt install wireguard
 3
 4# Debian (Bullseye and newer; older releases need backports enabled first)
 5sudo apt install wireguard
 6
 7# Fedora
 8sudo dnf install wireguard-tools
 9
10# Arch Linux
11sudo pacman -S wireguard-tools

On Arch specifically, the install page notes that users running kernels older than 5.6 may need wireguard-lts or wireguard-dkms paired with the matching linux-headers package instead of the plain wireguard-tools package, since older kernels don't carry the module built in. Red Hat Enterprise Linux and CentOS need an extra repository step first — either ELRepo's prebuilt kmod-wireguard package or a DKMS-based install — both documented in full on the install page above if your VPS runs one of those. Once the package lands, confirm the wg command is available:

1wg --version

Generating Keys with wg(8)

WireGuard authenticates peers with base64-encoded public/private key pairs instead of a certificate authority. The wg(8) manual page documents two dedicated subcommands for this: genkey generates a random private key, and pubkey reads a private key from standard input and derives the matching public key. WireGuard's quickstart guide gives the standard sequence:

1umask 077
2wg genkey > privatekey
3wg pubkey < privatekey > publickey

The umask 077 first is worth keeping — it makes sure the private key file is created readable only by you, not world-readable by default. You can also chain the two commands into one line: wg genkey | tee privatekey | wg pubkey > publickey. Every device in the tunnel — the VPS acting as server, and each client connecting to it — needs its own private/public pair generated this same way.

One detail from the manual page worth knowing before you move on: when configuring a peer via the wg set command directly (rather than a config file), both private-key and preshared-key must be passed as file paths, not literal key strings on the command line — because command-line arguments are visible to other processes on most systems (anyone running ps), and a private key typed directly into a shell command isn't private anymore.

Building the Interface and Bringing It Up

With keys generated, create the WireGuard network interface itself. The quickstart guide uses ip-link(8) to add it and ip-address(8) to assign it an address:

1sudo ip link add dev wg0 type wireguard
2sudo ip address add dev wg0 10.0.0.1/24

Then configure the interface with keys, listen port, and peer information using wg set, following the form the manual page documents:

1sudo wg set wg0 \
2  listen-port 51820 \
3  private-key /path/to/privatekey \
4  peer <PEER_PUBLIC_KEY> \
5  allowed-ips 10.0.0.2/32 \
6  endpoint 203.0.113.10:51820

allowed-ips does double duty in WireGuard: it's both a routing rule (which destination addresses get sent to this peer) and a firewall rule (which source addresses are accepted from this peer), so it should be no broader than the addresses you actually expect that peer to use. Finally, bring the interface up:

1sudo ip link set up dev wg0

At this point the tunnel is live. Rather than issuing all of the above by hand every time, most setups instead write a configuration file once and load it — the next section covers that format.

The Configuration File Format

Instead of a long wg set command, WireGuard also accepts a plain INI-style configuration file, applied with wg setconf <interface> <file>. The wg(8) manual page defines exactly two section types: one [Interface] section describing the local device, and one or more [Peer] sections describing who it talks to. Its own documented example looks like this:

1[Interface]
2PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
3ListenPort = 51820
4
5[Peer]
6PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
7Endpoint = 192.95.5.67:1234
8AllowedIPs = 10.192.122.3/32, 10.192.124.1/24

PrivateKey and ListenPort are the only two [Interface] fields most setups need — ListenPort is optional and gets chosen randomly if you omit it. In [Peer], PublicKey is required, Endpoint tells this side where to reach that peer (optional if the peer will initiate contact instead), and AllowedIPs sets both the route and the firewall filter described above. An optional PresharedKey field, generated with wg genpsk, layers an additional symmetric key on top of the public-key exchange for extra resistance against future cryptographic attacks — it's not required, but it's cheap to add.

The Arch Linux wiki's WireGuard page walks through the same fields in a multi-peer example and adds one more generation step worth copying if you're setting up more than two peers: a unique pre-shared key per peer pair, generated once with wg genpsk and referenced only by that pair, rather than one shared key reused everywhere.

Firewall, NAT, and Persistent Keepalives

By design, WireGuard tries to be quiet — per the quickstart guide, it only transmits when a peer actually has data to send and goes silent otherwise. That's efficient, but it creates a problem if your VPS is talking to a peer sitting behind NAT or a stateful firewall: NAT mappings expire when traffic stops flowing, so a client that's gone quiet may become unreachable from the server side until it sends something first.

The fix is PersistentKeepalive, a per-peer setting that sends an empty authenticated packet at a fixed interval to keep the NAT/firewall mapping open even when there's nothing real to transmit. It's off by default (value 0), and WireGuard's own documentation calls 25 seconds "a sensible interval that works with a wide variety of firewalls." Add it to a peer's [Peer] block only if that peer needs to receive unsolicited traffic from behind NAT — a VPS with a public IP acting purely as a server generally doesn't need it for itself, but a laptop or phone connecting in from behind a home router or mobile carrier NAT usually does.

Separately, remember that WireGuard runs over UDP on whatever ListenPort you configured (51820 is the conventional default but any port works) — your VPS's firewall needs to permit inbound UDP on that port, or none of the above will connect regardless of how correct the keys and config are.

Verifying the Tunnel

Once the interface is up — whether you built it by hand or loaded it from a config file — wg with no arguments shows the running state:

1sudo wg show

Per the manual page, this prints the interface's public key, listening port, and a per-peer summary showing endpoint, allowed IPs, and — once traffic has actually flowed — the latest handshake time and bytes transferred in each direction. wg showconf <interface> prints the same information back out in the config-file format if you need to confirm exactly what's loaded. The Arch Linux wiki suggests the simplest functional test once two peers are configured: ping the other side's internal tunnel address (ping 10.0.0.2 in the examples above) and confirm the reply comes back before trusting the tunnel with real traffic.

To cleanly remove an interface — for testing iterations, or tearing down before rebuilding a config — delete it the same way you created it:

1sudo ip link delete dev wg0

wg-quick: Skipping the Manual Steps

Running ip link add, ip address add, wg set, and ip link set up separately every time gets old fast. WireGuard ships a companion tool for exactly this, referenced directly in the quickstart guide: wg-quick, which automates that same bring-up-and-tear-down sequence from a single configuration file, so day-to-day operation is a matter of pointing it at the config you already wrote in the section above rather than re-running each ip and wg command by hand.

Who Should Self-Host a Tunnel Like This

Good fit:

  • A VPS admin who wants a VPN endpoint under full personal control, with no ongoing subscription
  • Site-to-site links between two servers you administer, or a point-to-site tunnel back into a home or office network
  • Anyone already comfortable managing SSH key pairs — WireGuard's key workflow follows the same mental model

Not the best fit:

  • Anyone who wants a polished consumer app with one-click server switching across many countries — that's the job commercial VPN services are built for
  • Teams that need built-in traffic logging or compliance dashboards without building that tooling themselves
  • Occasional, infrequent VPN use where standing up and maintaining a VPS isn't worth the ongoing admin overhead

A Tunnel You Fully Control

The whole setup comes down to four things: a key pair per device, a config file with one [Interface] and one or more [Peer] sections, a firewall rule opening the UDP port you chose, and — if any peer sits behind NAT — a PersistentKeepalive value so the connection survives being quiet. None of it requires a certificate authority or a vendor account, and every command above comes straight from WireGuard's own documentation and manual page rather than a third-party wrapper. Once wg show reports a recent handshake, the tunnel is live and doing exactly what it says on the box.

References

Posts in this series