Skip to content

Starting a WireGuard tunnel by hand with wg-quick up wg0 is fine for a first test. It is a poor way to live with a tunnel. The interface vanishes at every reboot, nothing restarts it when the network flaps, and nothing is written down for you to read when it fails at three in the morning. On almost every modern Linux distribution the answer is already installed: systemd. This article shows how to run WireGuard as a managed service, how to order it correctly at boot, how to reload it safely and how to troubleshoot it with the journal.

It assumes you already have a working configuration file and some comfort with the terminal. If you are still choosing between tunnel protocols, read WireGuard vs OpenVPN first, and for the general idea see what a VPN protocol is. All keys and addresses below are placeholders.

Two ways to run WireGuard under systemd

There are two supported routes, and it is worth picking one on purpose.

Approach How it works Best for
wg-quick service A template unit runs the wg-quick script, which creates the interface, adds addresses and routes and calls the DNS tooling Laptops, quick setups, most tutorials
systemd-networkd Plain .netdev and .network files describe the interface, and the network daemon creates it Servers, fixed setups, one place for all network config

The first is by far the most common, so we start there and come back to the second later. Whichever you choose, never manage the same interface with both. If wg0 is defined in networkd and you also run wg-quick@wg0, the two will fight over the addresses.

Setting up the wg-quick service

Before you start: a short checklist

Most service problems are really configuration problems that were never tested by hand. Before you hand the tunnel to systemd, confirm these points once.

  1. The tools are installed. On Debian-based systems the package is usually called wireguard or wireguard-tools, and on Fedora-family systems wireguard-tools. Run wg --version to confirm.
  2. The config works by hand. Run sudo wg-quick up wg0, test a page and a ping, then sudo wg-quick down wg0. If the manual run fails, the service will fail the same way and the journal will only repeat the error.
  3. The interface name is sensible. Linux limits interface names to 15 characters, so a config called my-very-long-office-tunnel.conf will not work.
  4. The address range is private and does not collide with your home or office network. RFC 1918 lists the blocks you can use.
  5. The firewall allows the tunnel's UDP port if the machine accepts incoming connections.

The config file itself

Place your configuration at /etc/wireguard/wg0.conf. The file holds a private key, so give it strict permissions:

sudo chown root:root /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/wg0.conf

WireGuard itself warns about world-readable configs, and for good reason. Then enable and start the service in one step:

sudo systemctl enable --now wg-quick@wg0

The name after the at sign is the config file name without .conf. This template design means that a second tunnel in office.conf is simply wg-quick@office. You can run several at once as long as their addresses and routes do not collide.

Check the result with three commands.

  1. systemctl status wg-quick@wg0 shows whether the unit is active. Because it is a one-shot unit that remains after exit, it normally says active (exited), which is correct and not an error.
  2. sudo wg show lists the interface, the peer and, crucially, the latest handshake line. A handshake within the last couple of minutes means traffic is flowing. No handshake line means the peer has never answered.
  3. ip -brief address show wg0 confirms the address was assigned.

Running more than one tunnel

A laptop user might keep a home tunnel and a work tunnel side by side. Each gets its own config file, its own interface name and its own service, for example wg-quick@home and wg-quick@work. The practical rule is that two full-tunnel configs cannot both own the default route. Give at most one of them 0.0.0.0/0 in AllowedIPs and restrict the other to its own network range. Enable only the ones you want at boot, and start the rest on demand with systemctl start.

To stop the tunnel, use systemctl stop wg-quick@wg0. To keep it from starting at boot, use systemctl disable wg-quick@wg0. Stopping the unit runs the matching tear-down, including any PostDown commands in your config.

Starting after the network is ready

The most common failure is a tunnel that works when started manually but fails at boot. The service started before the network was usable, or the Endpoint hostname could not be resolved yet. The fix is a drop-in override, which survives package updates because it never edits the packaged unit file. Create it with:

sudo systemctl edit wg-quick@wg0

and paste:

[Unit]
Wants=network-online.target
After=network-online.target nss-lookup.target

[Service]
Restart=on-failure
RestartSec=15

Save, then run sudo systemctl daemon-reload and restart the service. The Wants and After lines wait for a connected network. On desktop systems, make sure the matching wait service for your network daemon is enabled, otherwise the target is reached too early. Restart=on-failure is honoured for one-shot units on reasonably recent systemd versions and gives the tunnel another chance if the first try fails. If you see an error about the restart setting being ignored, your systemd is older, and you can use the health-check timer described below instead.

One more tip: a literal IP address in Endpoint removes the name lookup as a failure point entirely. The trade-off is that you must update the file if the server's address changes.

Tying other services to the tunnel

A tunnel is often not the goal. It is the road for something else, such as a download client, a backup job or a container. systemd can express that dependency so the dependent service never runs without the tunnel. Create a drop-in for that service, for example sudo systemctl edit mybackup.service:

[Unit]
After=wg-quick@wg0.service
BindsTo=wg-quick@wg0.service

After controls ordering. BindsTo is stronger than Requires: if the tunnel unit stops or fails, systemd stops the dependent service too. That behaves like a small service-level kill switch, because the job cannot keep sending traffic over the plain connection while the tunnel is down. For a broader view of the idea, see what a VPN kill switch is. Note that this stops one service. It does not block every program on the machine.

Reading the journal

When something fails, the journal is your first stop. These commands cover nearly every case.

  • journalctl -u wg-quick@wg0 -b shows everything the unit logged since the last boot.
  • journalctl -u wg-quick@wg0 -f follows new lines live while you restart the service in another terminal.
  • journalctl -u wg-quick@wg0 --since "10 min ago" narrows the window.
  • systemctl status wg-quick@wg0 -l prints the last lines together with the exit status.

Typical messages and what they mean:

Message or symptom Likely cause Fix
Name or service not known Endpoint hostname not resolvable yet Order after network-online, or use an IP
RTNETLINK answers: File exists The interface or route already exists Stop any manual wg-quick session first
resolvconf: command not found A DNS = line but no resolver tool Install a resolvconf provider or remove the line
Unit active, no handshake Wrong keys, wrong port or blocked UDP Compare public keys, check the firewall and port
Configuration file ... is world accessible Permissions too open chmod 600 the file

Reloading without tearing the tunnel down

Restarting the unit drops every connection that uses the tunnel. For common edits, such as adding a peer or changing an allowed range, WireGuard can apply the difference live. The usual pattern is:

sudo wg syncconf wg0 <(sudo wg-quick strip wg0)

wg-quick strip removes the wg-quick-only lines such as Address, DNS and PostUp, leaving the pure WireGuard settings that wg syncconf understands. Existing sessions continue and only the difference changes. Anything wg-quick handles itself, namely addresses, routes, DNS and hooks, still needs systemctl restart. To make systemctl reload do this for you, add a drop-in with an ExecReload= line that runs the command above through /bin/bash -c.

A small health-check timer

Tunnels can go quiet without any service failing, for example after a laptop wakes on a different network. A simple timer can notice and restart the tunnel. Create /etc/systemd/system/wg-check.service:

[Unit]
Description=Check WireGuard tunnel

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'ping -c 2 -W 3 10.8.0.1 || systemctl restart wg-quick@wg0'

and /etc/systemd/system/wg-check.timer:

[Unit]
Description=Run the WireGuard check every minute

[Timer]
OnBootSec=2min
OnUnitActiveSec=1min

[Install]
WantedBy=timers.target

Replace 10.8.0.1 with an address that only answers through the tunnel, such as the peer's inner address. Enable with sudo systemctl enable --now wg-check.timer. Restarting the tunnel interrupts open connections, so use a generous interval and a check that is reliable. A ping can fail when a server blocks ICMP, in which case use a different probe.

The native alternative: systemd-networkd

If your server already uses systemd-networkd, you can describe WireGuard in the same files as everything else and skip wg-quick. Create /etc/systemd/network/50-wg0.netdev:

[NetDev]
Name=wg0
Kind=wireguard

[WireGuard]
PrivateKeyFile=/etc/systemd/network/wg0.key
ListenPort=51820

[WireGuardPeer]
PublicKey=PEER_PUBLIC_KEY
Endpoint=203.0.113.10:51820
AllowedIPs=10.8.0.0/24
PersistentKeepalive=25

and /etc/systemd/network/50-wg0.network:

[Match]
Name=wg0

[Network]
Address=10.8.0.2/24

Keep the key file readable only by root and the systemd-network group, for example mode 0640 owned by root:systemd-network. Apply with sudo networkctl reload and check with networkctl status wg0 and wg show. The networkd route does not run shell hooks the way PostUp does, so extras like firewall rules belong in separate units. For full-tunnel setups, consult the options for firewall marks and route tables in the systemd.netdev and systemd.network manuals before relying on it.

Common mistakes

  • Running wg-quick up by hand and the systemd unit at the same time.
  • Leaving the config readable by everyone.
  • Assuming active (exited) is a failure. For this unit it is the normal healthy state.
  • Editing the packaged unit file instead of using systemctl edit.
  • Forgetting daemon-reload after changing a unit.
  • Using a hostname endpoint with no network-wait ordering.
  • Using a private range that overlaps your home network. Pick another block from RFC 1918.

Summary

Running WireGuard as a systemd service gives you boot-time start, ordering after the network, logs in the journal and clean dependencies for the services that rely on it. Use wg-quick@name for simplicity, a drop-in for ordering and restarts, wg syncconf for live edits and journalctl to understand failures. For servers that already live in systemd-networkd, the native files keep everything together.

If you prefer to use a ready-made app on Linux, the Linux setup guide explains the free VPNclient installation, and the pricing page shows what is available.

Frequently asked questions

What does wg-quick@wg0 mean in systemctl commands?

wg-quick@.service is a template unit. The text after the at sign is the name of a config file, so wg-quick@wg0 reads /etc/wireguard/wg0.conf and creates the interface wg0. A file called office.conf would be controlled with wg-quick@office.

How do I make WireGuard start at boot?

Run sudo systemctl enable --now wg-quick@wg0. The enable part creates the boot-time link and the now part starts it immediately. Check with systemctl is-enabled wg-quick@wg0 and wg show.

Can I change the config without dropping the tunnel?

Yes for most peer and key changes. Use wg syncconf wg0 with the output of wg-quick strip wg0, which applies the difference without tearing the interface down. Address, route and DNS changes still need a full restart.

Why does the service fail at boot but work when I start it later?

It usually starts before the network is ready, or it cannot resolve a hostname in Endpoint. Order it after network-online.target with a drop-in override, or use a literal IP address for the endpoint.

Should I use wg-quick or systemd-networkd?

wg-quick is simpler and most guides assume it. systemd-networkd keeps everything in the same files as the rest of your network configuration and suits servers and fixed setups. Either is fine, but do not run both for the same interface.

About the author

Bob Kanokphon Thongdee, founder of XPASS VPN

Bob Kanokphon Thongdee

  • Founder of XPASS VPN
  • Co-founder of eSIMonline.asia
  • More than 7 years in online marketing
  • Founder of Cojourney Visa and Cojourney Travel

Need a VPN for a short trip or a busy month?

XPASS sells prepaid passes for a set number of days. You pay once and nothing renews automatically.

See passes

Keep reading

VPN Security

WireGuard vs OpenVPN Explained

WireGuard and OpenVPN both build encrypted tunnels, but they differ in design, complexity and flexibility. Here is a plain comparison and how to think about the choice.

9 min read

VPN Basics

What Is a VPN Protocol?

A VPN protocol sets the rules for how your device and a VPN server connect and protect traffic. Here is what that means in plain language, with the main trade-offs.

9 min read

Troubleshooting

WireGuard Speed Tests: What to Measure

Throughput is only one of six things worth measuring on a WireGuard tunnel. Here is a repeatable method, a worked example and the traps that make results misleading.

10 min read

VPN Security

What Is a VPN Kill Switch?

A kill switch stops your device from quietly falling back to an unprotected connection when the VPN drops. Here is how the idea works and how to check it.

12 min read

Troubleshooting

VPN Not Connecting? Step-by-Step Fixes

A clear order of checks for when the VPN will not connect: pass status, login details, Wi-Fi and mobile data, permissions, firewalls, app conflicts and error messages.

8 min read