Hosting Your Own VPN on a £1 VPS

I’ve long been a fan of self-hosted infrastructure, but until recently, my VPNs were always built for internal access. This time, I decided to go a step further, building my own personal VPN server for privacy and IP/location masking.

RackNerd recently dropped a £1/month VPS deal via LowEndBox (not sponsored), and I couldn’t resist the temptation to spin something up.

VPS Specs

Here’s what you get for just over a tenner per year:

  • 1x vCPU Core
  • 1GB RAM
  • 20GB SSD Storage
  • 2TB Bandwidth
  • 1Gbps Port
  • Dedicated IPv4
  • Full Root Access
  • KVM Virtualisation

This setup is more than enough to run a lean, secure WireGuard VPN.


Quick Setup Guide

1. SSH In & Update

ssh [email protected]
passwd  # change root password
apt-get update && apt-get upgrade -y

2. Install Docker & Docker Compose

apt-get install -y ca-certificates curl gnupg lsb-release

mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
  $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io

# Install Docker Compose
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

docker --version
docker-compose --version

Set Up SSH Key Auth

On your local machine:

ssh-keygen -t rsa -b 4096 -C "[email protected]"
ssh-copy-id [email protected]

Or do it manually if ssh-copy-id isn’t available.

Then disable password auth on the server:

nano /etc/ssh/sshd_config

Make sure these lines are set:

PermitRootLogin yes
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no

Restart SSH:

systemctl restart ssh

Test your key login before closing your current session!


Deploy WireGuard with wg-easy

First, generate a secure password hash:

docker run ghcr.io/wg-easy/wg-easy wgpw 'YOUR_PASSWORD'

Take note of the hash output, remember to double all dollar signs ($$) when pasting into docker-compose.yml as per below.


Sample docker-compose.yml

volumes:
  etc_wireguard:

services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy
    container_name: wg-easy
    environment:
      - LANG=en
      - WG_HOST=YOUR_SERVER_IP
      - PASSWORD_HASH=$$2a$$12$$abc123...  # use your generated hash
      - WG_PORT=51820
      - WG_DEFAULT_ADDRESS=10.8.0.x
      - WG_DEFAULT_DNS=8.8.8.8,1.1.1.1
      - WG_MTU=1420
      - WG_ALLOWED_IPS=0.0.0.0/0
      - WG_PERSISTENT_KEEPALIVE=25
      - UI_TRAFFIC_STATS=true
      - UI_CHART_TYPE=2
    volumes:
      - etc_wireguard:/etc/wireguard
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: always
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1

Start the container:

docker-compose up -d

Enable IP Forwarding

echo 1 > /proc/sys/net/ipv4/ip_forward
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p

Set Up NAT (Masquerading)

iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

Persist iptables rules:

mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4

crontab -e

Add:

@reboot iptables-restore < /etc/iptables/rules.v4

Result: Your Own Private VPN

At this point, you’ve got a fully functioning WireGuard VPN that routes all your internet traffic via your VPS and hides your IP and location.

Manage it via a web UI (http://your.server.ip:51821) to create clients as described in my other post.

Minimal resources, maximum control.