Starting WireGuard Interfaces Automatically with Launchd on macOS
Published on 4 Aug 2021 · Filed in Tutorial · 586 words (estimated 3 minutes to read)In late June of this year, I wrote a piece on using WireGuard on macOS via the CLI, where I walked readers using macOS through how to configure and use the WireGuard VPN from the terminal (as opposed to using the GUI client, which I discussed here). In that post, I briefly mentioned that I was planning to explore how to have macOS’ launchd automatically start WireGuard interfaces. In this post, I’ll show you how to do exactly that.
These instructions borrow heavily from this post showing how to use macOS as a WireGuard VPN server. These instructions also assume that you’ve already walked through installing the necessary WireGuard components, and that you’ve already created the configuration file(s) for your WireGuard interface(s). Finally, I wrote this using my M1-based MacBook Pro, so my example files and instructions will be referencing the default Homebrew prefix of /opt/homebrew. If you’re on an Intel-based Mac, change this to /usr/local instead.
The first step is to create a launchd job definition. This file should be named <label>.plist, and it will need to be placed in a specific location. The <label> value is taken from the name given to the job itself, which you’ll see in the example job definition below. Since this modifies the networking configuration of your macOS system, it will need to be treated as a “global daemon” and will need to be placed in the /Library/LaunchDaemons directory.
Here’s an example of a job definition:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.wireguard.wg0</string>
<key>ProgramArguments</key>
<array>
<!-- Points to local version of wg-quick that
fixes path issues with the script -->
<string>/Users/slowe/.local/bin/wg-quick</string>
<string>up</string>
<string>wg0</string>
</array>
<key>KeepAlive</key>
<dict>
<key>NetworkState</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/opt/homebrew/var/log/wireguard.err</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<!-- Adds in user-specific and Homebrew bin directories to start of PATH -->
<string>/Users/slowe/.local/bin:/opt/homebrew/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
</dict>
</plist>
For the most part, the job definition is pretty easy to figure out, but here’s a few notes:
- The
<label>reference in naming the file pertains to the value of theLabelkey in the job definition. In my example, I’m calling the job “com.wireguard.wg0”, so the filename should becom.wireguard.wg0.plist. Note that the extension is necessary. - Note in the
ProgramArgumentskey that I’m referencing my personal changed copy of the Homebrewwg-quickscript, which works around some path- and shell-related issues on M1-based Macs (as described here). - As mentioned earlier, users of Intel-based Macs would want to change references to
/opt/homebrewover to/usr/local(and the reference to the local copy ofwg-quickmay not be necessary).
Make sure this job definition is saved to an appropriate file name (<label>.plist) and it’s found in the /Library/LaunchDaemons directory. Then run these commands to inform launchd of the new job definition you just created:
sudo launchctl enable system/com.wireguard.wg0
sudo launchctl bootstrap system /Library/LaunchDaemons/com.wireguard.wg0.plist
After running these two commands, you should be able to run wg show and see your WireGuard interface up and running. It should get turned up every time you restart your computer and there is a network present/active.
Additional Resources
In addition to the Barrowclift post, I also found the launchd.info site to be quite helpful.
Although the above instructions work without any issues on my Mac, I can’t guarantee they’ll work on every system out there. If you run into issues, or if you find that I’ve provided incorrect or incomplete information, please let me know. You can interact with me on Twitter, or drop me an e-mail (my address isn’t too hard to find).