Skip to content

WireGuard VPN deployment on AWS via Terraform

Notifications You must be signed in to change notification settings

hwhang0917/wireguard-aws

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WireGuard VPN on AWS (Tokyo Region)

A cost-effective WireGuard VPN setup on AWS EC2 with auto-shutdown when idle.

Features

  • Docker-based: Uses wg-easy for easy management
  • Web UI: Manage clients via browser
  • Auto-shutdown: Instance stops after 30 minutes of no VPN traffic
  • Cost-effective: ~$1-2/month with minimal usage (t4g.nano ARM instance)
  • Elastic IP: Fixed IP address persists across stop/start

Prerequisites

  • AWS CLI configured (aws configure)
  • Terraform installed
  • Docker (for generating password hash)
  • SSH key pair registered in AWS Tokyo region (ap-northeast-1)

Setup

1. Clone and configure

cd wireguard-aws
cp terraform.tfvars.example terraform.tfvars

2. Register SSH key in AWS (if not already done)

# Check existing keys
aws ec2 describe-key-pairs --region ap-northeast-1 --query 'KeyPairs[*].KeyName'

# Import your key if needed
ssh-keygen -y -f ~/.ssh/your-key.pem > ~/.ssh/your-key.pub
aws ec2 import-key-pair \
  --region ap-northeast-1 \
  --key-name "your-key-name" \
  --public-key-material fileb://~/.ssh/your-key.pub

3. Generate password hash

# Generate and base64 encode in one command
docker run --rm ghcr.io/wg-easy/wg-easy wgpw 'YourSecurePassword' | cut -d"'" -f2 | base64 -w0
echo  # newline

4. Get your public IP

curl ifconfig.me

5. Edit terraform.tfvars

key_name                = "your-ssh-key-name"
wg_password_hash_base64 = "your-base64-encoded-hash"
my_ip                   = "YOUR.PUBLIC.IP/32"

6. Deploy

terraform init
terraform plan
terraform apply

7. Wait and connect

Wait 2-3 minutes for the instance to initialize, then:

terraform output

Open the Web UI URL in your browser and login with your password.

Usage

Web UI

  • Access: http://<vpn_ip>:51821
  • Create clients, download configs, scan QR codes

Connecting from Linux

# Install WireGuard
sudo pacman -S wireguard-tools  # Arch
sudo apt install wireguard      # Debian/Ubuntu

# Save config from Web UI to /etc/wireguard/wg0.conf

# Connect
sudo wg-quick up wg0

# Check status
sudo wg

# Disconnect
sudo wg-quick down wg0

Connecting from Mobile

  1. Install WireGuard app (iOS/Android)
  2. Scan QR code from Web UI
  3. Toggle connection on/off

Managing the Instance

Check status

aws ec2 describe-instances \
  --instance-ids $(terraform output -raw instance_id) \
  --region ap-northeast-1 \
  --query 'Reservations[0].Instances[0].State.Name' \
  --output text

Start instance (after auto-shutdown)

aws ec2 start-instances \
  --instance-ids $(terraform output -raw instance_id) \
  --region ap-northeast-1

# Wait for it
aws ec2 wait instance-running \
  --instance-ids $(terraform output -raw instance_id) \
  --region ap-northeast-1

Stop instance manually

aws ec2 stop-instances \
  --instance-ids $(terraform output -raw instance_id) \
  --region ap-northeast-1

SSH into instance

$(terraform output -raw ssh_command)

Helper Scripts (Optional)

Create these in ~/.local/bin/ for convenience:

wg-vpn-start

#!/bin/bash
INSTANCE_ID=$(cd ~/wireguard-aws && terraform output -raw instance_id)
REGION="ap-northeast-1"

echo "Starting WireGuard VPN..."
aws ec2 start-instances --instance-ids "$INSTANCE_ID" --region "$REGION" > /dev/null
aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" --region "$REGION"

IP=$(terraform -chdir=~/wireguard-aws output -raw vpn_ip)
echo "VPN ready: http://$IP:51821"

wg-vpn-stop

#!/bin/bash
INSTANCE_ID=$(cd ~/wireguard-aws && terraform output -raw instance_id)
aws ec2 stop-instances --instance-ids "$INSTANCE_ID" --region ap-northeast-1 > /dev/null
echo "VPN stopping..."

wg-vpn-status

#!/bin/bash
INSTANCE_ID=$(cd ~/wireguard-aws && terraform output -raw instance_id)
STATE=$(aws ec2 describe-instances \
  --instance-ids "$INSTANCE_ID" \
  --region ap-northeast-1 \
  --query 'Reservations[0].Instances[0].State.Name' \
  --output text)
echo "Status: $STATE"

Make executable:

chmod +x ~/.local/bin/wg-vpn-{start,stop,status}

Cost Breakdown

Resource Monthly Cost
t4g.nano (stopped) $0
t4g.nano (running) ~$3.80/month full-time
Elastic IP (attached to running instance) $0
Elastic IP (attached to stopped instance) ~$3.60/month
EBS 30GB gp3 ~$2.40/month

Typical usage (2 hrs/day): ~$6-8/month

Note: AWS charges for Elastic IPs attached to stopped instances since February 2024.

Auto-Shutdown

The instance automatically shuts down after 30 minutes of no VPN activity. This is handled by a cron job that checks WireGuard handshake timestamps.

To modify the idle threshold, SSH into the instance and edit:

sudo nano /opt/wireguard/auto-shutdown.sh
# Change IDLE_THRESHOLD=1800 (seconds)

Troubleshooting

Can't access Web UI

  1. Check instance is running
  2. Verify your IP matches my_ip in terraform.tfvars
  3. Check security group allows your IP

Container not starting

ssh -i ~/.ssh/your-key.pem ec2-user@<IP>
sudo docker ps -a
sudo docker logs wg-easy
cat /var/log/user-data.log

Password not working

The password hash may have been corrupted. SSH in and regenerate:

cd /opt/wireguard
sudo docker-compose down

HASH=$(sudo docker run --rm ghcr.io/wg-easy/wg-easy wgpw 'YourPassword' | cut -d"'" -f2)
export PASSWORD_HASH="$HASH"
export PUBLIC_IP=$(curl -s ifconfig.me)

sudo -E docker-compose up -d

Cleanup

terraform destroy

This removes the EC2 instance, security group, and Elastic IP.

License

MIT

About

WireGuard VPN deployment on AWS via Terraform

Resources

Stars

Watchers

Forks