Skip to content
application · network · vpn

Tunnels without the bloat,WireGuard.

A fast, modern, open-source VPN protocol that builds encrypted point-to-point tunnels with state-of-the-art cryptography and a tiny codebase. This guide walks through the core peer-and-key model, the smallest useful two-machine tunnel, a full Kubernetes DaemonSet deployment, Docker, and mesh automation with Netmaker.

Peers, not clients

WireGuard's model is symmetric — every device is a peer with a key pair, and the public keys themselves form the access control list. One UDP port, a handful of config lines, and the tunnel is up.

  • Keys — public keys are the ACL; generate with wg genkey.
  • Routing — AllowedIPs decides what flows through each peer.
  • NAT — PersistentKeepalive holds firewall mappings open.
UDP 51820Transport
~4k LOCCodebase
ChaCha20Crypto
25sKeepalive

Start here

Information

WireGuard is a modern, high-performance VPN application designed to be simple, fast, and secure. It utilizes state-of-the-art cryptography to establish encrypted connections between devices, ensuring privacy and security over the internet. Unlike traditional VPN solutions, WireGuard is lightweight, with a minimal codebase that reduces the potential for vulnerabilities and improves performance. It is cross-platform, supporting major operating systems like Linux, Windows, macOS, iOS, and Android, and is known for its ease of configuration and seamless integration into existing network infrastructures. WireGuard’s efficiency and robust security make it an ideal choice for both personal and enterprise use.

Core concepts

How WireGuard Works

WireGuard connects peers, not clients and servers — the model is symmetric. Each peer has a key pair: a private key it keeps secret and a public key it shares. A peer accepts an encrypted packet only if it is signed by a known public key listed in its config, so the public keys themselves form the access control list.

Three fields do most of the work in every config:

FieldMeaning
PrivateKeyThis peer’s secret key (from wg genkey)
PublicKeyThe remote peer’s public key, under [Peer]
AllowedIPsWhich IPs are accepted from and routed to that peer
EndpointThe remote peer’s reachable IP:port
PersistentKeepaliveSeconds between keepalives to hold a NAT mapping open

Hands on

Basic Peer-to-Peer Tunnel

Before the Kubernetes deployment, here is the smallest useful setup — two machines linked over a single UDP port. Generate a key pair on each peer:

Terminal window
umask 077
wg genkey | tee privatekey | wg pubkey > publickey

Server (/etc/wireguard/wg0.conf):

[Interface]
PrivateKey = <Server_Private_Key>
Address = 10.0.0.1/24
ListenPort = 51820
[Peer]
PublicKey = <Client_Public_Key>
AllowedIPs = 10.0.0.2/32

Client (/etc/wireguard/wg0.conf):

[Interface]
PrivateKey = <Client_Private_Key>
Address = 10.0.0.2/24
[Peer]
PublicKey = <Server_Public_Key>
Endpoint = server.example.com:51820
AllowedIPs = 10.0.0.0/24
PersistentKeepalive = 25

Bring the tunnel up on both ends with wg-quick up wg0 (and down with wg-quick down wg0). Check live status and handshake times with sudo wg. Setting the client’s AllowedIPs to 0.0.0.0/0 instead would route all its traffic through the server — a full VPN.

  1. Tutorial:

    This is a step-by-step tutorial on setting up WireGuard for a Kubernetes cluster to enable access to containers from different networks via WireGuard!

  2. Prepare Kubernetes Cluster:

    Ensure that you have a running Kubernetes cluster. You can use a managed service like GKE, EKS, or AKS, or set up your cluster using tools like Minikube, kubeadm, or k3s.

  3. Install WireGuard on Nodes:

    Install WireGuard on all Kubernetes nodes. The following instructions are for Ubuntu, but they can be adapted for other distributions.

    Terminal window
    sudo apt update
    sudo apt install -y wireguard
  4. Configure WireGuard Keys:

    Generate WireGuard keys on each node.

    This example below shows how to generate keys on a single node:

    Terminal window
    umask 077
    wg genkey | tee privatekey | wg pubkey > publickey

    Repeat this step for each node in the cluster and keep a record of each node’s private and public keys.

  5. Create WireGuard Configuration:

    Create a WireGuard configuration file for each node. Below is an example configuration (wg0.conf) for a node:

    [Interface]
    PrivateKey = <Node_Private_Key>
    Address = 10.0.0.1/24
    ListenPort = 51820
    [Peer]
    PublicKey = <Peer_Node_Public_Key>
    Endpoint = <Peer_Node_IP>:51820
    AllowedIPs = 10.0.0.2/32
    PersistentKeepalive = 25
    • Replace <Node_Private_Key> with the node’s private key.
    • Replace <Peer_Node_Public_Key> with the peer node’s public key.
    • Replace <Peer_Node_IP> with the peer node’s IP address.
    • Adjust the Address and AllowedIPs fields as needed.
  6. Deploy WireGuard DaemonSet:

    Create a Kubernetes DaemonSet to deploy WireGuard on all nodes.

    Save the following YAML to a file named wireguard-daemonset.yaml:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
    name: wireguard
    namespace: kube-system
    spec:
    selector:
    matchLabels:
    name: wireguard
    template:
    metadata:
    labels:
    name: wireguard
    spec:
    hostNetwork: true
    containers:
    - name: wireguard
    image: k8s.gcr.io/pause:3.1 # Use a pause container
    securityContext:
    privileged: true
    volumeMounts:
    - name: wireguard-config
    mountPath: /etc/wireguard
    - name: lib-modules
    mountPath: /lib/modules
    volumes:
    - name: wireguard-config
    hostPath:
    path: /etc/wireguard
    - name: lib-modules
    hostPath:
    path: /lib/modules

    Deploy the DaemonSet:

    Terminal window
    kubectl apply -f wireguard-daemonset.yaml
  7. Create WireGuard ConfigMap:

    Create a ConfigMap to store the WireGuard configuration files. Save the following YAML to a file named wireguard-configmap.yaml, and include the configuration for each node:

    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: wireguard-config
    namespace: kube-system
    data:
    wg0.conf: |
    [Interface]
    PrivateKey = <Node1_Private_Key>
    Address = 10.0.0.1/24
    ListenPort = 51820
    [Peer]
    PublicKey = <Node2_Public_Key>
    Endpoint = <Node2_IP>:51820
    AllowedIPs = 10.0.0.2/32
    PersistentKeepalive = 25
    [Peer]
    PublicKey = <Node3_Public_Key>
    Endpoint = <Node3_IP>:51820
    AllowedIPs = 10.0.0.3/32
    PersistentKeepalive = 25

    Replace the placeholders with actual keys and IPs.

    Apply the ConfigMap:

    Terminal window
    kubectl apply -f wireguard-configmap.yaml
  8. Create WireGuard Init Container:

    Modify the DaemonSet to include an init container that sets up WireGuard.

    Update wireguard-daemonset.yaml:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
    name: wireguard
    namespace: kube-system
    spec:
    selector:
    matchLabels:
    name: wireguard
    template:
    metadata:
    labels:
    name: wireguard
    spec:
    hostNetwork: true
    initContainers:
    - name: setup-wireguard
    image: busybox
    command: ["sh", "-c", "cp /etc/wireguard-config/wg0.conf /etc/wireguard/wg0.conf && wg-quick up wg0"]
    volumeMounts:
    - name: wireguard-config
    mountPath: /etc/wireguard-config
    - name: wireguard
    mountPath: /etc/wireguard
    containers:
    - name: wireguard
    image: k8s.gcr.io/pause:3.1
    securityContext:
    privileged: true
    volumeMounts:
    - name: wireguard
    mountPath: /etc/wireguard
    - name: lib-modules
    mountPath: /lib/modules
    volumes:
    - name: wireguard-config
    configMap:
    name: wireguard-config
    - name: wireguard
    hostPath:
    path: /etc/wireguard
    - name: lib-modules
    hostPath:
    path: /lib/modules

    Update the DaemonSet:

    Terminal window
    kubectl apply -f wireguard-daemonset.yaml
  9. Verify WireGuard Setup:

    Check the status of WireGuard on each node:

    Terminal window
    sudo wg

    You should see the WireGuard interface wg0 and its peers.

  10. Access Containers Across Networks:

    Now, your nodes are connected via WireGuard. You can access containers across different networks by using the WireGuard IP addresses.

    For example, if you have a pod on Node 1 with a WireGuard IP of 10.0.0.1, you can access it from Node 2 using that IP.

  11. Conclusion:

    You have successfully set up WireGuard on a Kubernetes cluster, enabling secure communication between containers across different networks. This setup can be expanded and customized to fit more complex networking requirements.

Containers

Docker

Installing WireGuard on Docker!

  • Docker Compose

    • Github src=“data/wireguard/docker-compose.yml” description=“This is a docker compose for wireguard.”
      • Embed is disabled as of right now.
  • Ubuntu Installation Guide

    • Core Pre-Installation
      • Make sure your docker install is setup! If you need more information, please visit our Docker application page.
      • Check your firewall, are you using ufw , iptables or nftables
    • Firewall
      • Wireguard will be operating on the UDP port of 51820.
      • For: ufw
        • To enable the port through ufw run sudo allow 51821/udp

Mesh automation

Netmaker

  • Netmaker is a Wireguard automation application that handles self-hosted homelabs to small business / enterprise networking.
  • Official Github Repo
  • Advance install for netmaker allows the setup of a highly available installation within Kubernetes through helm.

  • The default settings may not install wireguard at the kernel level (for security reasons) and default to Postgres for storage.

    • Not having kernel level wireguard may cause performance drops and they recommend that you install wireguard before beginning.
  • Helm Install Commands:

    • Terminal window
      helm repo add netmaker https://gravitl.github.io/netmaker-helm/
      helm repo update
    • If you do not have helm or kubernetes setup, we recommend you visit our kubernetes setup.

  • The storage of the certificates will be an issue for this netmaker cluster, they recommend two types of storage classes:

    • RWO - Read Write Once - Storage instance where only a single node is allowed to access the storage volume at a time for read and write access.
    • RWX - Read Write Many - Storage instance where many nodes can concurrently read and write to the storage volume.

Questions

Frequently asked

What is WireGuard?

WireGuard is an open-source VPN protocol that creates fast, encrypted point-to-point tunnels. It uses modern cryptography, runs in the Linux kernel for high performance, and has a very small codebase — a few thousand lines — which reduces its attack surface.

How does WireGuard differ from OpenVPN?

WireGuard is far smaller and faster. It runs in the kernel with modern fixed cryptography and connects almost instantly, whereas OpenVPN runs in userspace with configurable ciphers and higher overhead. WireGuard configs are also much simpler.

What are AllowedIPs in a WireGuard config?

AllowedIPs serves two roles — it defines which source IPs are accepted from a peer (a cryptographic access control list) and which destination IPs are routed through that peer. Setting 0.0.0.0/0 routes all traffic through the tunnel.

What is PersistentKeepalive in WireGuard?

PersistentKeepalive sends a small packet every N seconds to keep a NAT or firewall mapping open. It is needed when a peer sits behind NAT and must remain reachable; a value of 25 seconds is the common default.

What port does WireGuard use?

WireGuard uses a single UDP port, 51820 by default, set with ListenPort. Because it is UDP-only, you must allow that port through firewalls like ufw, iptables, or nftables on the server side.