You want a VPN you own. No annual "no-logs" audit, no IP shared with 4,000 strangers, no price doubling when the promo ends. This guide gets WireGuard running on a Contabo VPS in 20 minutes, from signup to the first curl ifconfig.me returning your German VPS IP.
We've been running this exact config in production for 14 months. Zero unplanned tunnel incident, ~200 Mbps stable, ~24 ms latency Paris ↔ Nuremberg.
Step 1 — Order the Contabo VPS
Go to contabo.com via our link /go/contabo and pick:
- Plan: VPS S Cloud (4 vCPU AMD EPYC, 8 GB RAM, 50 GB NVMe, 200 Mbps guaranteed, 32 TB/mo traffic)
- Commitment: 24 months (locks the price at 4.99 €/mo, monthly is 8.49 €)
- OS: Ubuntu 22.04 LTS (24.04 works too, but 22.04 stays more stable for WireGuard packages)
- Datacenter: Nuremberg (DE) — best France/Europe latency + German GDPR jurisdiction
- Root password: generate a strong one, stash temporarily in your password manager (delete after creating non-root user)
- Add-ons: untick everything (Contabo backups not needed, we back up ourselves)
Total upfront ~119 € over 24 months. On activation you receive an email with the public IP and root password. Plan 5 minutes to 4 hours depending on time. Peak hours (6pm-10pm CET, Tuesday/Wednesday) are slowest.
Disclosure:
/go/contabois a sponsored link. If you grab the VPS, we earn a commission at zero cost to you. Our pricing is unaffected. We wouldn't write this guide if we weren't using Contabo ourselves.
Step 2 — First SSH and hardening
In your local terminal:
ssh root@YOUR.PUBLIC.IP
# Accept fingerprint, paste root password
Once connected, update first:
apt update && apt upgrade -y
apt install -y curl wget unzip fail2ban
Create a non-root user (SSH root will be disabled):
adduser eric
# Follow prompts (strong password, optional full name)
usermod -aG sudo eric
Copy your SSH public key from your local MacBook (in another terminal):
ssh-copy-id eric@YOUR.PUBLIC.IP
# If ssh-copy-id is missing: cat ~/.ssh/id_ed25519.pub | ssh root@IP "mkdir -p /home/eric/.ssh && cat >> /home/eric/.ssh/authorized_keys && chown -R eric:eric /home/eric/.ssh && chmod 700 /home/eric/.ssh && chmod 600 /home/eric/.ssh/authorized_keys"
Test non-root login in a new terminal without closing the first:
ssh eric@YOUR.PUBLIC.IP
# Should login with SSH key passphrase only
If that works, disable root SSH and password auth in /etc/ssh/sshd_config (from the root shell):
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
Now port 22 stays open but SSH-key only and only for eric. No more root brute-force risk.
Step 3 — UFW firewall
# Still as root
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp comment 'SSH'
ufw allow 51820/udp comment 'WireGuard'
ufw enable
# Answer y
ufw status verbose
UFW is now active. Log out of the root session and continue as eric with sudo.
Step 4 — Install WireGuard
# As eric with sudo
sudo apt install -y wireguard qrencode iptables-persistent
Generate server keys:
sudo bash -c 'umask 077 && wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub'
sudo cat /etc/wireguard/server.pub
# Note the pubkey, you'll need it for clients
Identify the public interface (could be eth0, ens3, ens18, etc.):
ip route | awk '/default/ {print $5}'
# On Contabo usually eth0 or ens18
Step 5 — Configure /etc/wireguard/wg0.conf
Edit (adjust eth0 if different above):
sudo nano /etc/wireguard/wg0.conf
Paste this (replace SERVER_PRIVATE_KEY with the contents of /etc/wireguard/server.key):
[Interface]
PrivateKey = SERVER_PRIVATE_KEY
Address = 10.66.66.1/24
ListenPort = 51820
MTU = 1420
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
Enable ip_forward:
sudo bash -c 'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf'
sudo sysctl -p
# Verify: sysctl net.ipv4.ip_forward (should say 1)
Start WireGuard:
sudo systemctl enable --now wg-quick@wg0
sudo wg show
# Should show interface wg0, public key, listening port
Step 6 — Create the first peer (client)
On the server, generate a client key:
sudo mkdir -p /etc/wireguard/clients
sudo bash -c 'umask 077 && wg genkey | tee /etc/wireguard/clients/mac.key | wg pubkey > /etc/wireguard/clients/mac.pub'
sudo cat /etc/wireguard/clients/mac.pub
# Note this pubkey
Add the peer to server wg0.conf:
sudo bash -c 'cat >> /etc/wireguard/wg0.conf <<EOF
[Peer]
# MacBook
PublicKey = CLIENT_MAC_PUBKEY
AllowedIPs = 10.66.66.2/32
EOF'
Reload config without down:
sudo wg syncconf wg0 <(wg-quick strip wg0)
Generate the client file (transfer securely to MacBook/iPhone):
sudo bash -c 'cat > /etc/wireguard/clients/mac.conf <<EOF
[Interface]
PrivateKey = $(cat /etc/wireguard/clients/mac.key)
Address = 10.66.66.2/24
DNS = 9.9.9.9, 149.112.112.112
MTU = 1420
[Peer]
PublicKey = $(cat /etc/wireguard/server.pub)
Endpoint = YOUR.PUBLIC.IP:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF'
For iPhone / Android, encode as QR:
sudo qrencode -t ansiutf8 < /etc/wireguard/clients/mac.conf
Scan this QR from the WireGuard app (App Store, Play Store) and the tunnel is configured.
For macOS / Linux desktop, fetch the .conf locally:
# On your Mac
scp eric@YOUR.PUBLIC.IP:/etc/wireguard/clients/mac.conf ~/Downloads/
# Import from official WireGuard app
Step 7 — Leak tests
Enable the tunnel on your client, then in a browser:
- ipleak.net: your public IP must be the Contabo Nuremberg VPS. Geo = Germany. If you still see your French ISP IP → tunnel isn't routing, check client
AllowedIPs. - dnsleaktest.com → "Extended test" button: returned DNS must be Quad9 (9.9.9.9) or your VPS. If you see Orange/SFR/Free DNS → DNS leak, check
DNS = 9.9.9.9in client.conf. - browserleaks.com/webrtc: no local IP must leak via WebRTC. If leaking → disable WebRTC in the browser (uBlock Origin → "Prevent WebRTC from leaking").
curl ifconfig.mefrom client terminal: must return the VPS IP.
All green: your self-host VPN works. Welcome.
Step 8 — Backups and maintenance
The tunnel runs. Good habits:
- Backup
/etc/wireguard/weekly:rsync -avz eric@VPS:/etc/wireguard/ ~/backup-wg/$(date +%F)/ - Monthly updates:
sudo apt update && sudo apt upgrade -y && sudo reboot(~30s downtime, or useunattended-upgrades) - Monitoring: see the Prometheus + Grafana monitoring guide
- Client-side kill switch: see the Linux kill switch (iptables + systemd) guide
- Specific-case templates: see the 8 ready-to-use WireGuard templates
Total cost over 5 years
To honestly compare with a commercial VPN service:
| Option | 24-month cost | 5-year cost (extrapolated) | Bandwidth |
|---|---|---|---|
| Contabo VPS S Cloud (self-host) | 119 € | ~298 € | 200 Mbps, 32 TB/mo |
| NordVPN 2 yrs + yearly renewal | 72 € | ~600 € | "Unlimited" shared |
| ExpressVPN 1 yr + renewal | 100 € | ~750 € | "Unlimited" shared |
And on the Contabo VPS you can also host: a Pi-hole DNS, Nextcloud, Vaultwarden, a Discord bot, a personal Mastodon instance. The VPN itself uses ~1% of CPU and RAM in normal personal use.
Verdict after 14 months in prod
WireGuard on Contabo VPS S at 4.99 €/mo remains our #1 pick for self-host VPN. 20-min setup, ~200 Mbps stable, ~24 ms latency from Paris, German GDPR jurisdiction. The real downside: email-only support (3-6 h average reply), not ideal during urgent outage.
To get started: Contabo VPS S Cloud via our link /go/contabo. You can cancel within the first 30 days if the setup doesn't fit (we verified on a test account).
Our full Contabo VPS review details strengths and weaknesses across 5 categories (perf, price, RAM/CPU, support, dashboard) with the score breakdown.
★ Datacenter Nuremberg GDPR · ✓ IPv4 dédiée incluse · 200+ Mbps garantis
Get Contabo30 jours satisfait ou remboursé→