Easy WireGuard VPN Server for Your Kubernetes Cluster
Want quick, secure VPN access to your cluster—without a dedicated VPS? This guide shows you how to install a WireGuard VPN server inside your Kubernetes cluster (great for accessing Pod IPs, Service IPs, and internal FQDN).
If you’re aiming for production-grade, it’s best to run WireGuard on a separate, dedicated VPS. But for internal or dev environments, this method is perfect.
We will be using the wg-easy helm template
Step 1: Generate a Password Hash
Before installing, generate a bcrypt hash for your WireGuard UI password.
Run this command locally (requires Docker):
docker run ghcr.io/wg-easy/wg-easy:14 wgpw secret
PASSWORD_HASH='$2a$12$j.imiU1h7MBYJ3jVi09SYOpW9TNuPac0xIQ28sASV4KphAOU9n.YO'
Copy the PASSWORD_HASH value—you’ll need it soon.
Step 2: Get Your Node IP and Cluster DNS
You’ll need the external IP of your node (not an internal 10.x.x.x or 172.x.x.x address), and the cluster DNS IP.
Find them using:
kubectl get node -o wide
Next you'll need the DNS service provider of your cluster (mostly coredns, or kube-dns)
kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP
kube-dns ClusterIP 10.43.0.10
Note the EXTERNAL-IP (or INTERNAL-IP, if applicable) of your node.
Step 3: Add the Helm Repo and Create a Values File
Add the wg-easy Helm chart:
helm repo add wg-easy https://raw.githubusercontent.com/hansehe/wg-easy-helm/master/helm/charts
enabled: true
replicaCount: 1
environmentVariables:
WG_HOST: <your-node-ip>
PASSWORD_HASH: "<your-password-hash>"
WG_DEFAULT_DNS: "<your-cluster-dns-ip>"
securityContext:
capabilities:
add:
- NET_ADMIN
- SYS_MODULE
service:
type: NodePort
port: 51820
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 128Mi
volume:
enabled: true
size: 100m
wireguard.yaml
Step 4: Install WireGuard with Helm
Deploy into its own namespace:
helm upgrade --install --namespace wireguard --create-namespace wg-easy wg-easy/wg-easy --values ./wireguard.yaml
Step 5: Find Your Service Ports
WireGuard exposes two ports:
- 51820/UDP – the VPN port
- 51821/TCP – the UI port
List services to see which NodePorts were assigned:
kubectl get svc -n wireguard
Example output:
NAME TYPE CLUSTER-IP PORT(S)
wg-easy NodePort x.x.x.x 51820:30222/UDP,51821:30223/TCP
Here, 30222 is the public VPN port, 30223 is the public UI port.
Step 6: (Optional) Use LoadBalancer Instead of NodePort
If you have multiple nodes, switch the service type to LoadBalancer in your wireguard.yaml, then redeploy:
service:
type: LoadBalancer
After deployment, find your external IP with:
kubectl get svc -n wireguard
Update your WG_HOST in the values file to this IP, and re-run the helm upgrade command.
environmentVariables:
WG_HOST: <ip-of-your-node>
Step 7: Connect!
- Open the WireGuard UI at http://<external-ip>:<ui-port>, log in with your password, and create a client.
- Download the generated .conf file.

Important:
Update the Endpoint in your client config with the correct public port mapped to 51820/UDP.
For example:
Endpoint = 31.12.282.192:30222
Step 8: Test Your VPN
After connecting, you should have full access to the cluster’s Pod IPs, Service IPs, and FQDN (e.g., http://wg-easy.wireguard.svc.cluster.local:51821/).
This address is the kubernetes FQDN which resolves services inside the cluster, cool right?
Step 9: Secure the UI
For security, restrict the WireGuard UI so it’s not exposed to the public Internet.
kubectl edit svc wg-easy
Find and delete the http-ui port entry:
ports:
- name: udp-vpn
nodePort: 30763
port: 51820
protocol: UDP
targetPort: udp-vpn
# Remove or comment out the following
- name: http-ui
nodePort: 30654
port: 51821
protocol: TCP
targetPort: http-ui
Save and exit.
That’s it!
You now have secure VPN access into your Kubernetes cluster—no extra VPS required.
Enjoy full access to Pod and Service networking, cluster DNS, and more.
Questions or feedback? Leave a comment below!