Manage WireGuard with systemd on Linux
Turn a hand-started WireGuard tunnel into a proper service. Learn wg-quick units, boot ordering, drop-in overrides, safe reloads, a health-check timer and the native systemd-networkd alternative.
10 min readUpdated Oct 15, 2026By XPASS Editorial Team

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.
- The tools are installed. On Debian-based systems the package is usually called
wireguardorwireguard-tools, and on Fedora-family systemswireguard-tools. Runwg --versionto confirm. - The config works by hand. Run
sudo wg-quick up wg0, test a page and a ping, thensudo wg-quick down wg0. If the manual run fails, the service will fail the same way and the journal will only repeat the error. - The interface name is sensible. Linux limits interface names to 15 characters, so a config called
my-very-long-office-tunnel.confwill not work. - The address range is private and does not collide with your home or office network. RFC 1918 lists the blocks you can use.
- 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.
systemctl status wg-quick@wg0shows whether the unit is active. Because it is a one-shot unit that remains after exit, it normally saysactive (exited), which is correct and not an error.sudo wg showlists the interface, the peer and, crucially, thelatest handshakeline. A handshake within the last couple of minutes means traffic is flowing. No handshake line means the peer has never answered.ip -brief address show wg0confirms 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 -bshows everything the unit logged since the last boot.journalctl -u wg-quick@wg0 -ffollows 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 -lprints 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 upby 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-reloadafter 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.
References
About the author

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 passesKeep 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