DEV Community

Cover image for Setting Up a Secure VPN Server on Ubuntu and Docker (Windows)
ngemuantony
ngemuantony

Posted on

Setting Up a Secure VPN Server on Ubuntu and Docker (Windows)

Setting Up a Secure VPN Server on Ubuntu and Docker (Windows)

Table of Contents

Introduction

WireGuard is a lightweight Virtual Private Network (VPN) protocol that supports both IPv4 and IPv6 connections. A VPN allows you to securely route your internet traffic through a private encrypted tunnel over untrusted networks, such as public Wi-Fi.
WireGuard uses modern cryptographic key pairs (public and private keys) to establish secure encrypted tunnels between peers.


Why Deploy a VPN, and Why Choose WireGuard?

In modern distributed systems, exposing internal services such as databases, APIs, or development environments directly to the public internet introduces significant security risks. A VPN mitigates this by creating a secure, encrypted tunnel that restricts access to authenticated devices only.
Traditional VPN solutions like OpenVPN and IPsec are powerful but often suffer from:

  • Large and complex codebases
  • Slow connection handshakes
  • Higher resource consumption

WireGuard improves this model by being:

  • Lightweight and kernel-level integrated
  • Extremely fast with near-instant handshakes
  • Built on modern cryptography (ChaCha20, Poly1305)
  • Easy to audit due to its small codebase

This makes it ideal for cloud servers, homelabs, and containerized environments.


Key Terms and Core Concepts

WireGuard operates using a peer-to-peer model rather than a traditional client-server architecture.

Term Simple Definition Practical Meaning
Peer Any device connected using a cryptographic key All devices are equal nodes in the VPN
Asymmetric Cryptography Uses a private and public key pair Private key stays secret, public key is shared
Allowed IPs Defines what traffic is routed through a peer Controls routing and access rules
Endpoint Public IP and port of a peer Where connections are initiated
IP Forwarding Kernel feature allowing routing between networks Required for internet sharing through VPN
NAT Masquerading Translates private IPs to public IP Enables internet access for VPN clients

Prerequisites

  1. Machine to Run WireGuard You can use:
  • Cloud VPS (Ubuntu 22.04+ recommended)
  • Personal computer (Linux, Windows, macOS)
  • Homelab server (PC, mini-PC, NAS)

Admin (sudo) access is required.


  1. Internet Connectivity A publicly reachable IP is required.
  • Cloud servers → usually included
  • Home/homelab → requires:
  • Port forwarding OR
  • Dynamic DNS (DDNS)

  1. Required Network Port
  • UDP 51820 must be open

Examples:

  • Cloud → Security group/firewall rules
  • Home → Router port forwarding + OS firewall rule

  1. Windows Users (Optional) If using Windows:
  • Install Docker Desktop
  • Enable WSL 2 backend

🐧 Method 1: Native Deployment on Ubuntu Server

Step 1: Install WireGuard

First, ensure your local system repositories are updated and pull down the standard tools package.

sudo apt update && sudo apt upgrade -y
sudo apt install wireguard -y
Enter fullscreen mode Exit fullscreen mode

Step 2: Generate Server Cryptographic Keys

WireGuard relies on asymmetric public/private key pairs for peer authentication. Let’s isolate our runtime directory and generate these keys securely:

### Move to the WireGuard directory and enforce strict file permissions
cd /etc/wireguard
sudo umask 077

# Generate private key and derive the public key from it
wg genkey | sudo tee private.key
sudo cat private.key | wg pubkey | sudo tee public.key
Enter fullscreen mode Exit fullscreen mode

Example:

Step 3: Enable IP Forwarding

For your server to function as a gateway and route your client traffic out to the internet, you must explicitly enable IPv4 packet forwarding inside the Linux kernel.
Open /etc/sysctl.conf:

sudo nano /etc/sysctl.conf
Enter fullscreen mode Exit fullscreen mode

Uncomment or add the following line:

net.ipv4.ip_forward=1
Enter fullscreen mode Exit fullscreen mode

Apply the changes instantly:

sudo sysctl -p
Enter fullscreen mode Exit fullscreen mode

Step 4: Create Server Config

Determine your physical public network interface identifier by pulling active route profiles:

ip route | grep default | awk '{print $5}'
Enter fullscreen mode Exit fullscreen mode

(Common interface outputs include: eth0, enp1s0, wlan0, or ens3)
Example:

Cat and copy the output of /etc/wireguard/private.key . It will be used in the wireguard server interface configuration.

sudo cat /etc/wireguard/private.key
Enter fullscreen mode Exit fullscreen mode

Example:

Create the main wireguard interface mapping file /etc/wireguard/wg0.conf:

sudo nano /etc/wireguard/wg0.conf
Enter fullscreen mode Exit fullscreen mode

Server Configuration

Insert the following configuration into your WireGuard interface file, making sure to replace the placeholder values with those from your system. The PrivateKey identifies the WireGuard server and must be the private key you generated earlier. The Address defines the server’s internal VPN IP and subnet, while ListenPort specifies the UDP port WireGuard will use to accept incoming connections. The PostUp and PostDown rules automatically configure and clean up firewall and NAT settings when the tunnel starts and stops. These rules allow VPN traffic to be forwarded through the system and enable connected clients to access external networks by translating their private VPN IPs to the server’s outgoing network interface (for example, eth0). If your system uses a different network interface, ensure you update it accordingly to maintain proper routing and internet access.

[Interface]
PrivateKey=REPLACE_THIS_WITH_THE_OUTPUT_OF-/etc/wireguard/private.key-YOU_COPIED
Address=10.8.0.1/24
ListenPort=51820

# Automated routing engine (Swap 'eth0' with your actual interface string)
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
Enter fullscreen mode Exit fullscreen mode

Example:

Step 5: Configure Firewall Policies (UFW)

Ensure the host-level firewall does not obstruct incoming cryptographic handshakes:

### Open WireGuard UDP port and protect your SSH management interface
sudo ufw allow 51820/udp
sudo ufw allow OpenSSH

### Reload firewall assets safely
sudo ufw disable && sudo ufw enable
Enter fullscreen mode Exit fullscreen mode

Step 6: Start Wireguard

Bind the virtual network tunnel interface wg0 directly to the system initialization engine:

sudo systemctl start wg-quick@wg0
sudo systemctl enable wg-quick@wg0
Enter fullscreen mode Exit fullscreen mode

Verify operational runtime status:

sudo systemctl status wg-quick@wg0
Enter fullscreen mode Exit fullscreen mode

Example:

🐳 Method 2: Docker Deployment on Windows (via Docker Desktop)

This method runs WireGuard using Docker and wg-easy, simplifying management through a web UI.


Overview

Instead of manual installation, WireGuard runs inside a Docker container with a web-based dashboard.


Why Docker?

  • Avoids Windows driver complexity
  • Simplifies configuration
  • Provides a clean web UI
  • Uses WSL 2 for Linux networking layer

What is wg-easy?

A web-based interface that allows you to:

  • Create VPN users
  • Generate configs automatically
  • Scan QR codes for mobile setup
  • Manage peers visually

Step 1: Create Project Folder

mkdir C:\wireguard-server
cd C:\wireguard-server
Enter fullscreen mode Exit fullscreen mode

Step 2: Docker Compose File

version: "3.8"

services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:latest
    container_name: wg-easy

    environment:
      - WG_HOST=your.public.ip
      - PASSWORD=StrongPasswordHere
      - WG_DEFAULT_DNS=1.1.1.1,8.8.8.8

    volumes:
      - ./wg-easy:/etc/wireguard

    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"

    restart: unless-stopped

    cap_add:
      - NET_ADMIN
      - SYS_MODULE

    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv4.ip_forward=1
Enter fullscreen mode Exit fullscreen mode

Step 3: Start Container

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Access:

http://localhost:51821
Enter fullscreen mode Exit fullscreen mode

Client Onboarding

Native Ubuntu Client Generation Pattern

This section explains how to connect a new device (laptop or desktop) to your WireGuard VPN manually.

1. Get the Server Public Key

On the server machine, display and copy the public key:

cat /etc/wireguard/public.key
Enter fullscreen mode Exit fullscreen mode

Example:

  • This key identifies the server to all clients.
  • You will use it in the client configuration under PublicKey.

2. Create a Client Configuration File

On the client machine (or server for generation purposes), create a new file:

/etc/wireguard/client1.conf
Enter fullscreen mode Exit fullscreen mode

This file defines how the client connects to the VPN.

3. Generate or Add Client Keys

Each client must have its own private key.

  • PrivateKey (client): stays only on the client device
  • PublicKey (client): is added to the server (not shown here but required) Generate private key and derive the public key from it This command are meant to generate key for the client/peer devicerr cd /etc/wireguard wg genkey | sudo tee client_private.key sudo cat private.key | wg pubkey | sudo tee client_public.key

4. Edit the client configuration file replacing the placeholders with the real values.

sudo nano /etc/wireguard/client1.conf


/etc/wireguard/client1.conf

[Interface]
Client’s private key (unique per device)
PrivateKey=<CLIENT_PRIVATE_KEY>

### VPN IP address assigned to this client
Address=10.8.0.2/32

DNS servers used while connected to VPN
DNS=1.1.1.1,8.8.8.8


[Peer]
Server public key (copied from server)
PublicKey=<SERVER_PUBLIC_KEY>

Public IP or domain of your VPN server
Endpoint=<SERVER_PUBLIC_WAN_IP>:51820

Route ALL traffic through VPN (full tunnel mode)
AllowedIPs=0.0.0.0/0

Keeps connection alive behind NAT/firewalls
PersistentKeepalive=25
Enter fullscreen mode Exit fullscreen mode

Example:

5. Register this client identity with your active runtime server engine:

sudo wg set wg0 peer <CLIENT_PUBLIC_KEY> allowed-ips 10.8.0.2/24
Enter fullscreen mode Exit fullscreen mode

6. Check the peer logs by running this command:

sudo wg show
Enter fullscreen mode Exit fullscreen mode

Example:

Peer Configuration on Windows (Containerized Web Management Method)

This method uses a web-based WireGuard management interface (such as wg-easy) running inside a Docker container on Windows. It removes manual configuration complexity by providing a graphical dashboard for managing VPN clients.


1. Access the Web Dashboard

Open your browser and navigate to the WireGuard management interface:

  • Example: http://localhost:51821
  • Or your server IP: http://SERVER_IP:51821

This dashboard allows you to manage all VPN users (peers) visually.


2. Create a New Client (Peer)

Inside the dashboard:

  • Click “New Client”
  • Enter a clear and identifiable name (e.g., John-Laptop, Office-PC, Windows-Desktop)

This name is only for management purposes and helps you identify devices easily.


3. Generate Client Configuration

Once the client is created, the system automatically generates:

  • A unique private key
  • A VPN IP address
  • A ready-to-use configuration profile

4. Connect Using QR Code or Configuration File

You now have two connection options:

Option A: QR Code (Recommended for Mobile Devices)

  • Click “Show QR Code”
  • Scan it using the WireGuard mobile app (iOS or Android)
  • The VPN profile is imported automatically

Option B: Download Configuration File (For Windows/Linux/macOS)

  • Click “Download Configuration”
  • Save the .conf file
  • Import it into the WireGuard desktop application

Steps for Windows WireGuard client:

  1. Open WireGuard application
  2. Click “Import tunnel from file”
  3. Select the downloaded .conf file
  4. Activate the tunnel

5. Activate the VPN

After importing:

  • Click Activate
  • The client will connect to the VPN server automatically
  • Traffic will be routed based on the configuration (full or split tunnel)

Diagnostics and Troubleshooting

Issue 1: Connected but No Internet Traffic

Symptom:
The VPN shows as connected, but you cannot access external websites or send traffic through the tunnel.
Cause:
This usually happens when IP forwarding or NAT routing is not configured correctly.
Fix:

1. Verify IP forwarding is enabled:

cat /proc/sys/net/ipv4/ip_forward
Enter fullscreen mode Exit fullscreen mode
  • Expected output: 1

If the result is 0, enable it immediately.

2. Ensure your PostUp and PostDown rules match your real network interface (e.g., eth0, ens3, wlan0).

3. A mismatch here will break outbound routing.


Issue 2: VPN Connects but No Handshake Behind NAT (Home/Residential Networks)

Symptom:
The client connects but never completes a handshake, or connection drops frequently on unstable networks (e.g., mobile data, home Wi-Fi).
Cause:
Some routers and NAT devices silently drop inactive UDP sessions.
Fix:

Add the following line to the client configuration:

PersistentKeepalive = 25
Enter fullscreen mode Exit fullscreen mode




What this does (simple explanation):

  • Sends a small “heartbeat” every 25 seconds
  • Keeps NAT/firewall sessions open
  • Prevents connection timeouts on strict or unstable networks

Inspecting Live VPN Status (Ubuntu Server)

To check active peers and connection status in real time, use:

sudo wg show
Enter fullscreen mode Exit fullscreen mode




This displays:

  • Active peers
  • Latest handshake time
  • Data transfer (upload/download)
  • Connection health

Conclusion

WireGuard provides a fast, secure, and lightweight approach to building modern VPN infrastructures across cloud, homelab, and enterprise environments. By combining cryptographic peer-to-peer connections, simple configuration, and efficient routing rules, it significantly reduces the complexity found in traditional VPN solutions while maintaining strong security and performance. The deployment approach outlined in this documentation is aligned with industry best practices as demonstrated in official implementation guides such as the DigitalOcean WireGuard setup tutorial, which provides a foundational reference for secure server and client configuration on Ubuntu systems .

This implementation and documentation have been developed and structured under Tuinnov8 Software Solutions, a software engineering company focused on building modern, scalable, and intelligent digital systems. You can learn more about our work and solutions at www.tuinnov8.com.

Top comments (1)

Collapse
 
ngemuantony profile image
ngemuantony

Was this helpful?