Networking AWS

WireGuard VPN Server on AWS User Guide

| Product: WireGuard VPN Server

Overview

This guide covers the deployment and configuration of WireGuard on AWS using cloudimg AWS Marketplace images. WireGuard is a fast, modern and lean VPN that lives in the Linux kernel. It uses Curve25519 for key exchange and ChaCha20-Poly1305 for data encryption, and presents a single UDP listener with a small, stateless configuration file - no PKI, no certificate authority and no IKE daemon.

The image installs WireGuard from the official distribution repository (the kernel module is in-tree, so there is no DKMS build and no third-party repository) and configures it to come up automatically under systemd, so an encrypted tunnel endpoint is listening on UDP 51820 within minutes of launch.

Security by design - no baked secret. There is no server private key inside the image. A fresh Curve25519 server keypair is generated on this instance's first boot, written only into the root-only server configuration /etc/wireguard/wg0.conf (mode 0600), and never captured into the image. Every instance you launch mints its own unique server identity. The server public key and connection details are written to a root-only file for you to hand to your client devices.

Security by design - server-only by default. The appliance ships with zero peers configured, so it is a private server-only endpoint rather than an open relay. You add one [Peer] block per client device that you explicitly authorise. IP forwarding is enabled so the instance can route between connected peers.

What is included:

  • WireGuard preinstalled and run under systemd (wg-quick@wg0.service), listening on UDP 51820

  • A per-instance Curve25519 server keypair, generated on first boot and never baked into the image

  • A default 10.50.0.0/24 overlay (the server is 10.50.0.1), with IP forwarding enabled

  • A server-only default configuration (no peers), with an example peer block and optional NAT gateway rules ready to uncomment

  • Unattended security upgrades left enabled so the server keeps receiving patches

WireGuard tunnel active on UDP 51820

Prerequisites

  • Active AWS account, an EC2 key pair, a VPC and subnet in the target region

  • Subscription to the WireGuard listing on AWS Marketplace

  • A security group allowing TCP 22 (admin) and, from the clients you intend to serve, UDP 51820 (WireGuard)

Recommended instance type: m5.large (2 vCPU, 8 GB RAM). WireGuard is extremely lightweight; a busy gateway with many peers can move to a larger instance type.

Step 1: Launch from AWS Marketplace

Find WireGuard on AWS Marketplace, published by cloudimg, and choose Continue to Subscribe, then Continue to Configuration and Continue to Launch. Select the Ubuntu 24.04 delivery option, your instance type (m5.large), your key pair, and a security group that allows TCP 22 for administration plus UDP 51820 from the client networks you want to serve.

Step 2: Connecting to your instance

SSH in with your EC2 key pair as the login user for the OS variant you launched.

OS variant SSH login user Example
Ubuntu 24.04 ubuntu ssh -i your-key.pem ubuntu@<public-ip>
ssh -i your-key.pem ubuntu@<public-ip>

Step 3: First boot

On first boot the image generates a per-instance Curve25519 server keypair, writes /etc/wireguard/wg0.conf (mode 0600, root only), resolves the instance public IP via EC2 instance metadata for the connection note, and starts wg-quick@wg0. This completes within a minute.

Confirm the tunnel is active and listening:

sudo systemctl start wg-quick@wg0 2>/dev/null || true
sleep 1
sudo systemctl is-active wg-quick@wg0
sudo wg show wg0
ip -brief addr show wg0

The service reports active, wg show reports the listening port 51820 and this instance's server public key, and the wg0 interface carries 10.50.0.1/24.

Step 4: Retrieve your server public key and connection details

The per-instance server public key and connection details live in a root-only file. The server private key never leaves /etc/wireguard/wg0.conf and is never shown here.

sudo cat /stage/scripts/wireguard-credentials.log

The file holds SERVER_PUBLIC_KEY, the SERVER_ENDPOINT (this instance's public IP and UDP 51820), the server VPN address (10.50.0.1) and the overlay subnet (10.50.0.0/24). These values are unique to this instance and were generated on first boot - they are never baked into the image.

Per-instance server key, none baked

Step 5: Authorise a client device

On the client device (your laptop, phone or another server) generate a keypair. On Linux:

wg genkey | tee client-privkey | wg pubkey > client-pubkey

Send the client public key to the server. On the server, add a [Peer] block to /etc/wireguard/wg0.conf - one per device, with a unique AllowedIPs address in the overlay:

[Peer]
PublicKey = <client-public-key>
AllowedIPs = 10.50.0.2/32

Apply the change:

sudo systemctl reload wg-quick@wg0

Then on the client, write a WireGuard configuration that points at this server. Use the SERVER_PUBLIC_KEY and SERVER_ENDPOINT from Step 4:

[Interface]
PrivateKey = <client-private-key>
Address = 10.50.0.2/24

[Peer]
PublicKey = <server-public-key>
Endpoint = <server-public-ip>:51820
AllowedIPs = 10.50.0.0/24
PersistentKeepalive = 25

Bring the client tunnel up (wg-quick up <config> on Linux, or import the file into the WireGuard app on macOS, Windows, iOS or Android). Set AllowedIPs = 0.0.0.0/0 on the client instead if you want all of the client's traffic to route through the VPN.

Step 6: Confirm a peer has connected

Once a client connects, wg show lists the peer with a recent handshake and transfer counters, proving a real encrypted tunnel rather than just a bound listener:

sudo wg show wg0

A connected peer appears under the interface with its public key, latest handshake a few seconds ago, and non-zero transfer figures.

Real encrypted tunnel handshake

Step 7: Optional - route client internet traffic through the VPN

By default WireGuard runs as a private peer-to-peer overlay: peers can reach each other and the server, but not the wider internet through the server. To make the instance act as an internet gateway for your clients, uncomment the two NAT lines in /etc/wireguard/wg0.conf (replace eth0 with the real WAN interface from ip route show default):

PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

Reload the tunnel (sudo systemctl restart wg-quick@wg0), set AllowedIPs = 0.0.0.0/0 on the client, and the client's traffic egresses through the instance. IP forwarding is already enabled in the image.

Step 8: Security and configuration notes

  • Per-instance server key. The Curve25519 server private key is generated on first boot and lives only in /etc/wireguard/wg0.conf (0600 root:root). It is never baked into the image, so no two customer instances share a server identity.

  • Server-only by default. The image ships with no peers. It is not an open endpoint - a device can only connect once you add its public key as a [Peer].

  • The overlay is 10.50.0.0/24 with the server at 10.50.0.1. Give each client device a unique /32 address (10.50.0.2, 10.50.0.3, ...).

  • Firewall. Open UDP 51820 to the client networks you want to serve in the instance security group. Keep the rules as tight as your use case allows.

  • Managing the tunnel. Reload after editing the config with sudo systemctl reload wg-quick@wg0, or restart with sudo systemctl restart wg-quick@wg0. The service is enabled at boot.

Step 9: Managing the service

sudo systemctl status wg-quick@wg0 --no-pager

The tunnel is managed entirely through systemd and the wg CLI. There is no web interface - WireGuard is configured through the wg0.conf file and the wg/wg-quick tools.

Support

This image is published and supported by cloudimg. For assistance, contact cloudimg support through the AWS Marketplace listing. WireGuard is distributed under the GPL-2.0 license. WireGuard is a registered trademark of Jason A. Donenfeld; cloudimg is not affiliated with or endorsed by the WireGuard project or Jason A. Donenfeld.