// Reclaim your digital sovereignty.
// WHAT WE'RE BUILDING
In this tutorial, you'll set up your own WireGuard VPN server. WireGuard is a modern, fast, and secure VPN protocol that uses cutting-edge cryptography. Unlike older VPNs that are complex and slow, WireGuard is lightweight - the entire codebase is only about 4,000 lines. This makes it faster, more secure, and easier to audit.
// WHY THIS MATTERS
Every time you connect to public WiFi at a coffee shop, airport, or hotel, you're trusting that network with all your traffic. Anyone on that network can potentially intercept your passwords, emails, and browsing activity. A VPN encrypts all your traffic, making it unreadable to eavesdroppers. But commercial VPNs have a problem - they can see all your traffic too. When you run your own VPN, only you control it. No logs, no data sharing, no subscriptions.
This project teaches you about:
WireGuard was designed to be simpler and faster than existing VPN solutions. Here's why it matters:
Prerequisites
Before starting this tutorial, make sure you've completed:
If you haven't completed these, check out our other tutorials first!
A VPN (Virtual Private Network) creates an encrypted tunnel between your device and a server. All your internet traffic flows through this tunnel, making it invisible to anyone on your local network or your ISP (Internet Service Provider).
When you use a VPN, websites see the VPN server's IP address instead of your real IP address. This provides two key benefits:
WireGuard uses a simple but powerful architecture:
Your VPN will work like this:
âââââââââââââââ âââââââââââââââ âââââââââââââââ
â Your â â VPS â â Internet â
â Device âââââVPNâââ Server âââââââââââ World â
â â Tunnel â (WireGuard)â â â
âââââââââââââââ âââââââââââââââ âââââââââââââââ
All your traffic goes from your device â VPN server â internet. The return traffic comes back the same way. To the website you're visiting, the request appears to come from your VPS.
Let's compare WireGuard to other popular VPN solutions:
| Feature | WireGuard | OpenVPN | Commercial VPN |
|---|---|---|---|
| Speed | Very Fast | Moderate | Varies |
| Code Size | ~4,000 lines | ~600,000 lines | Proprietary |
| Security | Excellent | Good | Unknown |
| Setup Difficulty | Easy | Hard | Very Easy |
| Privacy | You control | You control | Provider sees all |
First, let's set up the project on your local machine. We'll use Docker to run WireGuard locally for testing.
$ mkdir -p ~/projects/wireguard-vpn # Create project folder $ cd ~/projects/wireguard-vpn $ mkdir -p config # Create config directory for persistent data
$ git init $ git config user.name "Your Name" $ git config user.email "you@example.com"
$ cat > .gitignore << 'EOF' # Configuration files with secrets config/peer*.conf config/server*.json # Environment files .env # Editor files .vscode/ .idea/ *.swp # OS files .DS_Store Thumbs.db EOF
We'll use linuxserver/wireguard, a well-maintained Docker image that makes WireGuard easy to set up. It handles all the complex key generation for you.
$ cat > docker-compose.yml << 'EOF' version: '3.8' services: wireguard: image: linuxserver/wireguard:latest container_name: wireguard cap_add: - NET_ADMIN - SYS_MODULE environment: - PUID=1000 - PGID=1000 - TZ=America/New_York - SERVERURL=auto - SERVERPORT=51820 - PEERS=5 - PEERDNS=auto - INTERNAL_SUBNET=10.13.13.0 - ALLOWEDIPS=0.0.0.0/0 volumes: - ./config:/config - /lib/modules:/lib/modules ports: - "51820:51820/udp" sysctls: - net.ipv4.conf.all.src_valid_mark=1 restart: unless-stopped EOF
Understanding the Configuration
cap_add: NET_ADMIN, SYS_MODULE - Required for WireGuard to create network interfacesSERVERURL=auto - Automatically detects your VPS IP addressSERVERPORT=51820 - Default WireGuard port (you can change this)PEERS=5 - Number of client configurations to generateALLOWEDIPS=0.0.0.0/0 - Route all traffic through VPN (full tunnel)/lib/modules - Kernel modules needed for WireGuard$ docker-compose up -d # Start the WireGuard container Creating network "wireguard-vpn_default" with the default driver Creating wireguard ... done
Give it a few seconds to generate keys, then check the logs:
$ docker-compose logs -f wireguard # Watch the logs
You'll see the server keys being generated. Once it's ready, check the config folder:
$ ls -la config/ drwxr-xr-x 5 user user 4096 Feb 25 10:00 . drwxr-xr-x 1 user user 4096 Feb 25 10:00 .. drownied.srht.site/ peer1/ peer2/ peer3/ peer4/ peer5/ server.conf wg0.conf
The container generates several important files:
server.conf - Your server's WireGuard configurationwg0.conf - The active network interface configurationpeer1/ through peer5/ - Client configurationsLet's look at the server configuration:
$ cat config/server.conf [Interface] Address = 10.13.13.1 PrivateKey = SERVER_PRIVATE_KEY_HERE ListenPort = 51820 [Peer] # peer1 PublicKey = PEER_PUBLIC_KEY_HERE AllowedIPs = 10.13.13.2/32 ... (more peers)
Each peer folder contains a QR code (for mobile) and a .conf file (for desktop). Let's look at peer1:
$ ls -la config/peer1/ peer1.conf peer1.png (QR code for mobile) peer1-preshared.key peer1.public.key peer1.private.key
Here's what a client config looks like:
$ cat config/peer1/peer1.conf [Interface] PrivateKey = PEER_PRIVATE_KEY Address = 10.13.13.2/32 DNS = 1.1.1.1 [Peer] PublicKey = SERVER_PUBLIC_KEY Endpoint = your-server-ip:51820 AllowedIPs = 0.0.0.0/0 PersistentKeepalive = 25
Client Configuration Explained
PrivateKey - The client's private key (keep secret!)Address - IP address inside the VPN networkDNS - DNS server to use (1.1.1.1 is Cloudflare)PublicKey - The server's public keyEndpoint - Your server's IP and portAllowedIPs - What traffic to route through VPN (0.0.0.0/0 = everything)PersistentKeepalive - Keep connection alive every 25 seconds (helps with NAT)$ docker-compose down # Stop the container
Your configuration is saved in the config folder. When you run this on your VPS, the same configuration will persist.
Now let's put your VPN on the internet. You'll need a VPS with at least 1GB RAM and a static IP address.
$ ssh your-username@your-vps-ip
your-username@vps:~$ sudo apt update
your-username@vps:~$ sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
your-username@vps:~$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
your-username@vps:~$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
your-username@vps:~$ sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
WireGuard uses UDP port 51820 by default. Let's open that port:
your-username@vps:~$ sudo ufw allow 22/tcp # Allow SSH your-username@vps:~$ sudo ufw allow 51820/udp # Allow WireGuard your-username@vps:~$ sudo ufw enable
On your local machine, commit and push to GitHub:
$ cd ~/projects/wireguard-vpn $ git add . && git commit -m "Add WireGuard VPN configuration" $ git remote add origin git@github.com:yourusername/wireguard-vpn.git $ git push -u origin main
On your VPS, clone the repository:
your-username@vps:~$ cd ~ && git clone git@github.com:yourusername/wireguard-vpn.git wireguard
Before running, let's customize the configuration for your VPS. You'll want to set the SERVERURL to your actual IP or domain:
your-username@vps:~$ cd ~/wireguard your-username@vps:~$ nano docker-compose.yml
Change SERVERURL=auto to your actual domain or IP:
- SERVERURL=your-domain.com
If you don't have a domain, use your VPS IP address:
- SERVERURL=your-vps-ip-address
your-username@vps:~$ docker-compose up -d # Start WireGuard
your-username@vps:~$ docker-compose logs -f wireguard # Watch the logs to see peer configurations being generated
Wait a moment for the keys to generate, then check the config:
your-username@vps:~$ ls -la config/peer1/
Copy the peer1.conf file to your local machine. You can use SCP:
$ scp your-username@your-vps-ip:~/wireguard/config/peer1/peer1.conf ~/Downloads/wg0.conf # Copy the config file to your Downloads folder
Important Security Note
Keep your peer configuration files private! They contain the private key that gives access to your VPN. Never share them or commit them to Git.
Make sure the config/peer*.conf entries in your .gitignore are correct before pushing to GitHub.
Now let's connect your devices to your new VPN. WireGuard is available for all major platforms.
Install WireGuard:
$ sudo apt install wireguard # Debian/Ubuntu $ sudo pacman -S wireguard-tools # Arch Linux $ sudo dnf install wireguard-tools # Fedora
Copy your configuration file to the WireGuard directory:
$ sudo cp ~/Downloads/wg0.conf /etc/wireguard/wg0.conf $ sudo chmod 600 /etc/wireguard/wg0.conf # Secure the private key
Start WireGuard:
$ sudo wg-quick up wg0 # Connect to VPN
Check your connection:
$ sudo wg interface: wg0 public key: YOUR_PUBLIC_KEY private key: (hidden) listening port: 51820 peer: SERVER_PUBLIC_KEY endpoint: your-vps-ip:51820 allowed ips: 0.0.0.0/0 latest handshake: 1 minute ago transfer: 1.2 KiB received, 1.4 KiB sent
Verify your IP changed:
$ curl ipinfo.io # Check your IP - should show your VPS location
To disconnect:
$ sudo wg-quick down wg0
Install WireGuard from Homebrew:
$ brew install wireguard-tools
Or download the official app from the App Store or wireguard.com.
Import your configuration:
Toggle the switch to connect.
Download WireGuard from wireguard.com.
Install and open the application:
Install the WireGuard app from the App Store.
To import your config:
Or use the QR code:
your-username@vps:~$ ls config/peer1/peer1.png # The QR code is stored here
On your iOS device:
Install WireGuard from Google Play Store or F-Droid.
Import your configuration the same way as iOS - either by QR code or by importing the .conf file.
Once connected, verify your IP address changed:
$ curl ipinfo.io { "ip": "your-vps-ip-address", "city": "New York", "region": "New York", "country": "US", "org": "AS-EXAMPLE-VPS-PROVIDER" }
The IP should now show your VPS location instead of your actual location!
Let's verify your DNS requests are going through the VPN:
$ curl https://dns.google/resolve?name=example.com # Test DNS resolution through VPN
Or visit dnsleaktest.com to check for DNS leaks.
By default, we're routing all traffic through the VPN (0.0.0.0/0). This is the most secure option, but you might want to only route specific traffic through the VPN.
Edit your peer configuration to only route specific IPs:
AllowedIPs = 10.13.13.0/24, 192.168.1.0/24
This would only route:
Regular internet traffic would go through your normal connection.
WireGuard can automatically disconnect if the VPN connection drops, preventing your real IP from being exposed:
# Add to your client config [Interface] ... PostUp = iptables -I OUTPUT ! -o wg0 -j DROP PostDown = iptables -D OUTPUT ! -o wg0 -j DROP
This drops all traffic that doesn't go through WireGuard when the VPN is active.
Need more than 5 client connections? Edit docker-compose.yml:
- PEERS=10
Then restart:
your-username@vps:~$ docker-compose down && docker-compose up -d
New peer configurations will be generated in peer6/, peer7/, etc.
If your VPS IP changes (dynamic IP), use a dynamic DNS service. Many providers offer free dynamic DNS:
Once you have a domain, update your peer's endpoint:
Endpoint = your-domain.ddns.net:51820
WireGuard works great over mobile data. The PersistentKeepalive setting (25 seconds) helps maintain the connection through NAT.
For best results on mobile:
your-username@vps:~$ cd ~/wireguard && docker-compose down your-username@vps:~$ docker-compose pull your-username@vps:~$ docker-compose up -d
your-username@vps:~$ docker-compose exec wireguard wg interface: wg0 public key: SERVER_PUBLIC_KEY private key: (hidden) listening port: 51820 peer: PEER1_PUBLIC_KEY endpoint: 123.45.67.89:51820 allowed ips: 10.13.13.2/32 latest handshake: 15 seconds ago transfer: 1.2 MiB received, 3.4 MiB sent peer: PEER2_PUBLIC_KEY endpoint: 98.76.54.32:51820 allowed ips: 10.13.13.3/32 latest handshake: 2 minutes ago transfer: 500 KiB received, 1.1 MiB sent
The "latest handshake" shows when each peer last successfully communicated. If it says "0 seconds ago" or very recent, the connection is active.
Can't connect?
docker-compose psdocker-compose logs -f wireguardConnection drops?
Slow speeds?
Back up your configuration regularly:
your-username@vps:~$ tar -czf wireguard-backup-$(date +%Y%m%d).tar.gz config/
Store this backup securely. If you lose your keys, you'll need to regenerate them.
Congratulations! You've set up your own VPN server!
Next Steps:
You now have complete control over your internet connection. No subscriptions, no logs, no tracking. Just secure, private internet access anywhere in the world.
The revolution will not be proprietary.