Skip to Content

Self-Host WireGuard: Your Own Private VPN in 15 Minutes

Self-Host WireGuard: Your Own Private VPN in 15 Minutes

Every time you connect to public Wi-Fi at a coffee shop, airport, or hotel, your traffic is exposed. Even at home, your ISP can see what sites you visit. A VPN solves this, but commercial VPNs cost money, log data, and limit your connections. WireGuard is a modern, lightweight VPN protocol that you can self-host in minutes — giving you full control, better performance, and zero recurring fees.

In this guide, you will deploy a WireGuard VPN server with Docker, generate client configurations, and connect your devices. By the end, you will have a private tunnel to your server from anywhere in the world.

Why Self-Host WireGuard?

  • Privacy: Your traffic passes through your own server. No third-party logging.
  • Performance: WireGuard is leaner and faster than OpenVPN or IPSec.
  • Cost: One cheap VPS covers unlimited clients.
  • Access: Securely reach your homelab services, databases, and internal APIs remotely.
  • Simplicity: The entire protocol is under 4,000 lines of code.

Prerequisites

  • A VPS or dedicated server with a public IP address.
  • Docker and Docker Compose installed.
  • Minimum 512 MB RAM and 1 vCPU (WireGuard is extremely lightweight).
  • UDP port 51820 open in your firewall.
  • A domain or subdomain (optional, for DNS convenience).

Step 1: Create the Docker Compose File

Create a directory for WireGuard and add your configuration:

mkdir -p ~/wireguard && cd ~/wireguard
cat <<'DC' > docker-compose.yml
services:
  wireguard:
    image: linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - SERVERURL=your-server-ip-or-domain
      - SERVERPORT=51820
      - PEERS=laptop,phone,tablet
      - PEERDNS=1.1.1.1,8.8.8.8
      - INTERNAL_SUBNET=10.13.13.0
      - ALLOWEDIPS=0.0.0.0/0
      - PERSISTENTKEEPALIVE_PEERS=all
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped
DC

Replace your-server-ip-or-domain with your server's public IP or a domain pointing to it. The PEERS variable defines how many client configs to generate — here we create three: laptop, phone, and tablet.

Step 2: Start the Container

Launch WireGuard with a single command:

docker compose up -d

The container will generate server and client keys, create peer configurations, and start the VPN interface. This takes about 10-15 seconds on first run.

Step 3: Retrieve Client Configurations

Each peer's configuration is stored in the ./config directory. To view a QR code for mobile setup:

docker exec -it wireguard /app/show-peer laptop

Replace laptop with phone or tablet for other peers. A QR code will appear in your terminal — scan it with the official WireGuard app on iOS or Android.

To get the full config file for desktop clients:

cat ./config/peer_laptop/peer_laptop.conf

Copy this into the WireGuard app on Windows, macOS, or Linux.

Step 4: Verify the Connection

On your client device, activate the VPN tunnel. Then check your public IP:

curl ifconfig.me

The returned IP should match your server's public IP — confirming all traffic is routing through the VPN.

On the server, verify active peers:

docker exec -it wireguard wg show

You will see each connected peer with its public key, endpoint, and latest handshake timestamp.

Step 5: Firewall and Security Hardening

Ensure your server's firewall allows UDP port 51820:

# UFW (Ubuntu/Debian)
sudo ufw allow 51820/udp

# FirewallD (CentOS/RHEL/Fedora)
sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --reload

If you use a cloud provider (AWS, DigitalOcean, Hetzner), also open port 51820/UDP in the security group or cloud firewall.

For additional hardening:

  • Disable password authentication on your VPS and use SSH keys only.
  • Keep the WireGuard image updated: docker compose pull && docker compose up -d
  • Restrict ALLOWEDIPS if you only need LAN access instead of full tunneling.

Step 6: Add More Peers Later

Need to add a new device? Stop the container, update the PEERS list, and restart:

docker compose down
# Edit docker-compose.yml and add a new peer name
docker compose up -d

The new peer configuration will be generated automatically in ./config.

Verification Checklist

  • Client public IP matches server IP when VPN is active.
  • wg show displays connected peers with recent handshakes.
  • DNS resolution works (test with nslookup google.com).
  • Traffic is encrypted — packet captures on public Wi-Fi show only WireGuard UDP packets.

Next Steps

  • Split tunneling: Modify ALLOWEDIPS to route only specific subnets through the VPN.
  • Mesh networking: Combine WireGuard with Headscale for a self-hosted Tailscale alternative.
  • Monitoring: Add Uptime Kuma to monitor your VPN endpoint availability.
  • Automation: Use Ansible or Terraform to provision WireGuard across multiple regions.
  • DNS filtering: Point PEERDNS to a Pi-hole or AdGuard Home instance for network-wide ad blocking.

A self-hosted VPN is one of the most practical infrastructure investments you can make. WireGuard's simplicity means you spend less time debugging configurations and more time getting work done securely — from anywhere.

Self-Host Uptime Kuma: Monitor Everything in 15 Minutes