Open-source project
WireGuard/wireguard-go avatar
WireGuard/wireguard-go

wireguard-go: the userspace WireGuard implementation for platforms without a kernel module

Mirror only. Official repository is at https://git.zx2c4.com/wireguard-go

4,427 stars1,695 forksGoMIT

At a glance

What is it?
wireguard-go is the Go implementation of WireGuard that runs the protocol as a userspace process on Linux, macOS, Windows, FreeBSD and OpenBSD. It is the right tool when the kernel module is unavailable, and the wrong one when it is.
Who is it for?
Adopt wireguard-go on macOS, FreeBSD, OpenBSD, or Windows builds where the kernel module is not available, and use it as a library rather than a binary in those cases. Do not put it on a Linux router that can load the kernel module, and do not expect sticky sockets on macOS, FreeBSD or OpenBSD.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 131 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem wireguard-go solves: WireGuard where there is no kernel module

WireGuard's reference implementation on Linux is a kernel module. The README is direct about this: on Linux you "should instead use the kernel module, which is faster and better integrated into the OS." That leaves a gap on operating systems where no in-kernel implementation exists, and on systems where you cannot load one. wireguard-go fills it by speaking the same protocol from a Go process. The README frames the Windows case the same way, pointing users at the "more fully featured Windows app" which consumes wireguard-go as a module rather than running the binary directly.

So the audience is narrower than the project name suggests. It is not people who want a WireGuard VPN on Linux, because they already have a better option. It is people building a client on macOS, FreeBSD or OpenBSD, people embedding WireGuard in a Go program, and people running Windows builds through the official app. The repository layout supports this reading: the top level splits into device/, tun/, ipc/, conn/, ratelimiter/, replay/ and tai64n/, which are the parts you would import or reason about separately, alongside a thin main.go.

How wireguard-go works: a TUN device, a control socket and a Go data path

The mechanism is visible from the README and the directory names. You run the binary with an interface name, it creates a TUN interface through the tun/ package, and it forks into the background. From then on the process owns the interface. The device/ package holds the WireGuard state machine, the cryptographic handshake and the peer table. Packets arrive from the TUN device, get encrypted in userspace, and leave through ordinary UDP sockets handled by conn/.

Control is out of band. The ipc/ package implements the Unix socket protocol that wg(8) speaks, which is why the README says you configure a running interface with wg(8) and the usual ip and ifconfig commands. That socket is also the shutdown mechanism on systems that cannot delete interfaces directly: the README states that removing /var/run/wireguard/wg0.sock with rm -f causes wireguard-go to shut down.

The remaining packages are the details that make a userspace implementation survive real traffic. replay/ implements the anti-replay window so reordered or duplicated packets are rejected. ratelimiter/ throttles handshake attempts, which matters more in userspace because the handshake is not free. tai64n/ produces the timestamp used in the handshake. rwcancel/ exists because Go's runtime cannot interrupt a blocked read on its own, so the process needs a way to cancel I/O when the interface goes away. That last package is a good indicator of how much of the work is about Go's I/O model rather than about WireGuard itself.

Building and running wireguard-go for the first time

The README says the build requires an installation of the latest version of Go, and gives the clone and make sequence. The Makefile generates version.go from git describe before calling go build, so building from a tarball without git metadata is a different path than building from a clone.

bash
$ git clone https://git.zx2c4.com/wireguard-go
$ cd wireguard-go
$ make

After make completes you have a wireguard-go binary in the repository root. The Makefile also defines an install target that copies it to $(DESTDIR)$(BINDIR), which defaults to /usr/bin, and a test target that runs go test ./... across the packages.

Creating an interface is one command. The README contrasts it with the kernel workflow: instead of ip link add wg0 type wireguard, you run the binary.

bash
$ wireguard-go wg0

The process forks into the background and the interface exists. To keep it in the foreground, which is what you want while debugging, pass -f or --foreground. For more logging, set the environment variable LOG_LEVEL=debug. The README does not list the accepted values beyond debug.

bash
$ wireguard-go -f wg0

From here the interface is unconfigured. You need wg(8) from wireguard-tools to add peers and keys, and ip(8) or ifconfig(8) to assign addresses and bring the link up. To tear it down, use ip link del wg0, or on a system that cannot delete interfaces directly, remove the control socket.

bash
$ rm -f /var/run/wireguard/wg0.sock

On macOS and OpenBSD the interface name is constrained. Darwin's utun driver and OpenBSD's tun driver do not accept arbitrary names, so the README says you must use utun[0-9]+ or tun[0-9]+ for an explicit name, or pass the bare utun or tun and let the kernel choose. When you let the kernel choose, defining WG_TUN_NAME_FILE makes wireguard-go write the chosen name into that file, which is the only practical way to discover it afterwards.

Where wireguard-go is the wrong tool: Linux, and anything that needs sticky sockets

The clearest limitation is stated by the project itself. On Linux, the kernel module is faster and better integrated, and the README tells you to use it. Running wireguard-go on a Linux box that can load the module means paying a userspace context switch on every packet for no benefit. If your reason for choosing it is that you did not want to load a module, that is a policy decision, not a technical one, and you should make it explicitly.

The second limitation is sticky sockets. The README says wireguard-go "does not yet support sticky sockets" on macOS, FreeBSD and OpenBSD. The phrase "does not yet" is the project's own, and it appears in all three platform sections. Sticky sockets are what let a peer keep its source port when the underlying route or address changes, which is what makes roaming between networks work cleanly. Without them, a client that moves from Wi-Fi to cellular can lose its mapping and need a fresh handshake. If your deployment depends on smooth roaming, this is the constraint to design around.

Platform quirks follow from the same sections. On macOS, fwmarks are not supported at all, and the README attributes that to Darwin limitations. On FreeBSD, fwmark is mapped to SO_USER_COOKIE. On OpenBSD, it is mapped to SO_RTABLE. Those are not equivalent features under different names; they are the closest available socket option on each system, and code that relies on fwmark semantics should be checked against the specific platform.

wireguard-go vs the kernel module, and vs BoringTun

The comparison people actually ask about is wireguard-go against the kernel implementation, and the project answers it in one line: on Linux the kernel module is faster and better integrated. The difference in approach is where the packet path lives. The kernel module runs inside the network stack, so a packet does not cross the user-kernel boundary to be encrypted. wireguard-go runs the entire data path in a Go process, so every packet does cross that boundary twice. That is the cost, and it buys portability to systems that have no in-kernel option.

The other comparison worth drawing is against BoringTun, Cloudflare's userspace WireGuard implementation in Rust. Both take the same architectural position: implement the protocol outside the kernel so it can run anywhere. The difference is the host language and what that implies for embedding. wireguard-go is a Go module, so a Go program can import it, and the repository layout with device/, tun/ and ipc/ as separate packages is arranged for that. BoringTun is a Rust crate, which suits Rust and C consumers. Neither is a drop-in for the other, and the choice usually follows from the language of the code you are already writing rather than from protocol behaviour, which both implement.

A third option is not a separate project at all. On Windows, the recommended path is the official Windows app, which per the README uses wireguard-go as a module. Choosing wireguard-go there means choosing to be the app, not to run alongside it.

Maintenance, upgrade cost and the MIT licence

The repository at WireGuard/wireguard-go is a mirror; the README states the official repository is at git.zx2c4.com/wireguard-go. That matters for upgrade planning, because issues and patches belong upstream, and a mirror can lag. The last push to the mirror was on 2026-05-22, which is recent enough that the code is not stale, but the mirror status is the thing to check before you file anything against it.

Upgrading is cheap by construction. The build produces a single static binary, so replacing it is a file copy, and the Makefile's install target does exactly that with install -v -m 0755. There is no configuration file format to migrate and no on-disk state beyond the control socket in /var/run/wireguard/, which is recreated at startup. The one upgrade hazard is interface naming on macOS and OpenBSD: if the kernel has been assigning utun or tun numbers and your scripts hardcode them, a restart can move the interface. WG_TUN_NAME_FILE exists for this reason, and using it is cheaper than debugging a script that assumed utun3.

The licence is MIT. The README carries the full permission notice, which allows use, modification, distribution, sublicensing and sale provided the copyright notice and permission notice are included. There is no copyleft obligation and no separate commercial licence to negotiate. The usual caveat applies: if you are redistributing wireguard-go inside a product, include the notice, and if your legal position is unusual, that is a question for a lawyer rather than for this article.

Editorial conclusion

Adopt wireguard-go on macOS, FreeBSD, OpenBSD, or Windows builds where the kernel module is not available, and use it as a library rather than a binary in those cases. Do not put it on a Linux router that can load the kernel module, and do not expect sticky sockets on macOS, FreeBSD or OpenBSD. Before deploying, verify that your kernel can load the WireGuard module on Linux, that your interface name matches the utun or tun rules on macOS and OpenBSD, and that wg(8) from wireguard-tools is installed, because wireguard-go creates the interface and nothing else configures it.

Frequently asked questions

What is wireguard-go?

It is an implementation of WireGuard in Go, published as a mirror of the official repository at git.zx2c4.com/wireguard-go. It runs the WireGuard protocol as a userspace process that creates a TUN interface, which is how it works on platforms that have no in-kernel implementation.

How do I install wireguard-go?

The README says the build requires an installation of the latest version of Go, then gives the sequence git clone https://git.zx2c4.com/wireguard-go, cd wireguard-go, make. The Makefile also provides an install target that places the binary in $(DESTDIR)$(BINDIR).

How do I use wireguard-go?

Run wireguard-go wg0 to create an interface and fork into the background, or add -f or --foreground to stay in the foreground. Once the interface is running, the README says you configure it with wg(8) plus the usual ip(8) and ifconfig(8) commands.

What is the difference between wireguard-go and the kernel module?

The README states that on Linux you should use the kernel module instead, because it is faster and better integrated into the OS. The kernel module encrypts inside the network stack, while wireguard-go runs the whole data path in a userspace process.

How does wireguard-go compare with BoringTun?

Both implement WireGuard outside the kernel, which is what makes them portable. wireguard-go is a Go module with device/, tun/ and ipc/ as separate packages, so it suits Go programs that want to embed it; BoringTun is a Rust implementation and suits Rust or C consumers.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. WireGuard/wireguard-go on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wireguard-wireguard-go.svg)](https://hysenlabs.com/projects/wireguard-wireguard-go)