Build a WireGuard VPN Gateway

Route your Mac's IPv4 traffic through a WireGuard gateway on DigitalOcean.

Introduction

30 Second Summary

Your Mac usually sends internet traffic through whichever network you are connected to. That leaves the route beyond your screen under the network owner's control.

In this project, you will build a personal IPv4 VPN gateway with WireGuard on a DigitalOcean Droplet. You will send your Mac's internet traffic through that server while learning how the tunnel moves packets.

What You'll Build

When your finished gateway is active, websites report your DigitalOcean Droplet's public IPv4 address as the source of your Mac's traffic.

By the end of this project, you'll have:

  • A private WireGuard tunnel where your Mac can reach 10.90.90.1 while the server reports an authenticated handshake.
  • A routing test that shows why a secure tunnel still needs IPv4 forwarding plus source NAT before internet traffic can leave the Droplet.
  • A reboot-ready full-tunnel connection that makes websites see the Droplet's public IPv4 address.
  • Secret Mission: Add a second Mac profile that reaches only 10.90.90.0/24 while normal internet traffic stays on your local connection.

Are there any prerequisites?

You need a Mac running macOS 12.0 or later.

You also need a DigitalOcean account with a payment method for the $4.00 per month Basic Droplet.

Before We Start

Before any hands-on work begins, lock in what you are building and why it matters. Your goal is an educational personal IPv4 VPN gateway for routing Mac traffic through a DigitalOcean server you control, with no promise of anonymity or complete IPv6 leak protection.

Set Up the Mac and VPN Server

A personal VPN gateway needs two reachable peers. Your Mac also needs a secure way to administer the DigitalOcean Droplet through SSH.

In this step, you prepare the Mac with WireGuard. You also create the Ubuntu server that becomes your VPN gateway.

In this step, get ready to:
  • Prepare the Mac for WireGuard access.
  • Create the personal-vpn Droplet with SSH authentication.
  • Protect the server before installing WireGuard tools.

Why WireGuard for this project?

WireGuard exposes tunnel interfaces directly. It also exposes peer keys.

Tailscale automates peer discovery. OpenVPN adds a certificate-authority workflow. Raw WireGuard keeps your attention on routing fundamentals.

Prepare the Mac and SSH key

The WireGuard app requires macOS 12.0 or later. Confirming compatibility first prevents an installation dead end.

  • Click the Apple menu in the upper-left corner of your screen.
  • Choose About This Mac.
  • Read the macOS version shown in the window.

Your Mac is compatible when the major version is 12.0 or later.

✔️ I see version 1.0.16

Your WireGuard app is current. Keep it installed for the tunnel you create in the next step.

ⓧ I see an older version

  • Use the available update control on the WireGuard listing.
  • Complete any Apple Account prompt that appears.
  • Return to the listing after the update finishes.
  • Confirm that the listing now shows version 1.0.16.

The updated app is ready to create and activate WireGuard tunnels.

ⓧ It is not installed yet

  • Use the install control on the WireGuard listing.
  • Complete any Apple Account prompt that appears.
  • Wait for the installation to finish.
  • Confirm that WireGuard is listed as installed.

WireGuard is now available on your Mac.

DigitalOcean provides the server and its Control Panel. You need an account with an accepted payment method before you can create the paid Droplet.

  • Open the DigitalOcean registration page if you need an account.
  • Complete the account verification steps shown by DigitalOcean.
  • Add an accepted payment method to the account.
  • Open the DigitalOcean sign-in page if you already have an account.
  • Sign in to reach the DigitalOcean Control Panel.

The Control Panel is ready for the server setup. Your next task is to confirm that the Mac has a public SSH key.

  • Press Cmd+Space to open Spotlight.
  • Type Terminal into Spotlight.
  • Press Enter to open Terminal.
  • Check for the default public key by running this command:
ls -l ~/.ssh/id_ed25519.pub

What does this command check?

The command checks whether ~/.ssh/id_ed25519.pub exists. This file contains the public half of your SSH key pair.

✔️ I see id_ed25519.pub

Your existing public key is ready. Copying this file is safe because it cannot recreate the private key.

  • Copy the existing public key to your clipboard by running this command:
pbcopy < ~/.ssh/id_ed25519.pub

What does this command do?

The command places the contents of ~/.ssh/id_ed25519.pub on the Mac clipboard. You paste that public key into DigitalOcean during Droplet creation.

ⓧ The file was not found

OpenSSH can generate a new Ed25519 key pair. The command asks for a save location before asking for an optional passphrase.

  • Generate the new key pair by running this command:
ssh-keygen

What does this command create?

The command creates a private key at ~/.ssh/id_ed25519. It creates the matching public key at ~/.ssh/id_ed25519.pub.

  • Press Enter to accept the default file location.
  • Enter a passphrase if you want the private key encrypted on disk.
  • Press Enter again if you choose an empty passphrase.
  • Re-enter the passphrase if you created one.
  • Stop immediately if Terminal asks whether to overwrite an existing key.
  • Copy the new public key to your clipboard by running this command:
pbcopy < ~/.ssh/id_ed25519.pub

What does this command do?

The command copies only the public key. Keep ~/.ssh/id_ed25519 private because it proves your identity to the server.

Having trouble with the SSH key?

  • Check that the filename ends in .pub before copying it.
  • Stop the key generator if it offers to overwrite an existing key.
  • Ask for help before changing an existing key: help me safely check or create my macOS SSH key.
Create the personal-vpn Droplet

A Droplet is DigitalOcean's Linux virtual machine. This one gives your VPN a public endpoint on the internet.

Before You Create the Droplet

This next action creates a paid resource. The Basic 512 MiB plan costs $4.00 per month.

DigitalOcean continues billing the Droplet while it is powered off. You stay in control because destroying it stops compute billing.

  • Switch back to the DigitalOcean Control Panel from earlier.
  • Click Create at the top of the Control Panel.
  • Select Droplet.
  • Choose a nearby location under Choose Region.
  • Select Ubuntu under Choose an image.
  • Choose Ubuntu 24.04 (LTS) x64.
  • Select Basic under Choose a Droplet Plan.
  • Select the 512 MiB plan.

Why This Droplet Size?

A WireGuard gateway for one Mac needs little memory or CPU. The Basic plan provides one virtual CPU with 500 GiB of included monthly transfer.

  • Confirm that Public IPv4 address is enabled.
  • Select SSH keys under Choose Authentication Method.
  • Click Add SSH Key if your key is not listed.
  • Paste the copied key into the Public Key field.
  • Enter personal-vpn in the Key Name field.
  • Click Add SSH Key to save the key.
  • Select the saved SSH key for this Droplet.
  • Leave the paid backup add-on unselected.

The operating system and access method are now configured. The final settings identify the server.

  • Enter personal-vpn as the Droplet name under Finalize Details.
  • Click Create Droplet.
  • Wait until the Droplet page lists its Public IPv4 address.
  • Record that address here: your-droplet-public-ip.

That is your VPN server online. Its public IPv4 address gives your Mac a reachable SSH destination.

Droplet creation not completing?

  • Confirm that the account has an accepted payment method.
  • Check that the selected region offers the Basic 512 MiB plan.
  • Ask for help with the exact screen you see: help me finish creating my DigitalOcean Droplet.
Protect and prepare the server

A DigitalOcean Cloud Firewall controls which traffic can reach the Droplet. Your rules keep administrative access limited while exposing the WireGuard endpoint.

  • Switch back to Terminal on your Mac.
  • Check the Mac's current public IPv4 address by running this command:
curl -4 ifconfig.me

What does this command show?

The command asks ifconfig.me to return the public IPv4 address used by your Mac. This address becomes the baseline for your later VPN test.

  • Record the returned address here: baseline-public-IPv4.

Your baseline is saved. The firewall can now limit SSH access to that single public address.

  • Switch back to the DigitalOcean Control Panel.
  • Click Create at the top of the Control Panel.
  • Select Firewall.
  • Enter personal-vpn in the Name field.
  • Use the existing SSH rule in Inbound Rules.
  • Remove All IPv4 from its Sources field.
  • Remove All IPv6 from its Sources field.
  • Enter baseline-public-IPv4 followed immediately by /32 in Sources.

Why Use a /32 Source?

CIDR notation describes an address range. A /32 matches one IPv4 address.

The SSH rule therefore accepts administrative connections from your current public address. Unmatched inbound traffic stays blocked.

  • Remove any other inbound rules from the creation form.
  • Click Add inbound rule.
  • Select Custom as the rule type.
  • Select UDP as the protocol.
  • Enter 51000 as the port.
  • Keep All IPv4 in the Sources field.
  • Remove All IPv6 from the UDP rule.
  • Retain the default outbound rules.

The inbound side now permits TCP port 22 from your Mac. It also permits UDP port 51000 from IPv4 clients.

  • Select personal-vpn in the Apply to Droplets field.
  • Click Create Firewall.
  • Click Networking in the Control Panel menu.
  • Select Firewalls.
  • Open the firewall applied to personal-vpn.
  • Select the Rules tab.
  • Confirm that only the SSH and WireGuard inbound rules are present.
  • Confirm that the default outbound rules remain present.
  • Select the Droplets tab.
  • Confirm that personal-vpn is protected by the firewall.

Your firewall is active. The server now accepts only the two inbound paths needed for this project.

Before connecting, do you expect Terminal to stay on your Mac or switch to the Ubuntu server?

  • Return to Terminal from earlier.
  • Approve the host key prompt if the first SSH connection asks for confirmation.
  • Connect to the Droplet as root by running this command:
ssh root@[[DROPLET_PUBLIC_IPV4="your-droplet-public-ip"]]

What does this command do?

The ssh command opens an encrypted administrative session as the Droplet's initial root user. Your local private key proves that this Mac holds the key selected during creation.

You'll see the Ubuntu welcome screen when the SSH connection succeeds. Commands entered in this session now run on personal-vpn.

  • Expect the package manager to request confirmation before installing files.
  • Install the WireGuard package by running this command on the Droplet:
sudo apt install wireguard

What does this command install?

The command installs the WireGuard package from Ubuntu's repositories. Ubuntu 24.04 provides the matching WireGuard tools package.

  • Approve the package manager's confirmation prompt if it appears.
  • Wait until the terminal prompt returns.

Before you check, what should a successful installation let the Droplet report?

  • Verify the WireGuard tools installation by running this command:
wg --version

What does this command verify?

The command asks the installed WireGuard tools for their version. A version response proves that the server-side tools are available.

You'll see a wireguard-tools version beneath the Ubuntu session output. That's the server access and package installation confirmed.

SSH or WireGuard command failing?

  • Confirm that the SSH firewall source matches your current public IPv4 address with /32 appended.
  • Check that the Droplet address matches your-droplet-public-ip.
  • Ask for help using the visible error: help me troubleshoot my Droplet connection or WireGuard installation.

✔️ Awesome, I've got everything!

Your Mac is ready. Your firewall-protected Ubuntu Droplet accepts SSH access and reports an installed WireGuard tools version.

ⓧ I'd like to double check the setup

Compare your setup with the required end state below.

  • The DigitalOcean Cloud Firewall is applied to personal-vpn. Inbound TCP port 22 is allowed only from the Mac baseline public IPv4 address as /32.
  • Inbound UDP port 51000 is allowed from All IPv4. The default outbound rules are retained.
  • The DigitalOcean Droplet is named personal-vpn. It uses the Basic 512 MiB plan in a nearby region.
  • The Droplet runs Ubuntu 24.04 LTS x64. Public IPv4 is enabled.
  • The learner's SSH key is attached to the Droplet. No paid backup add-on is enabled.
  • The WireGuard app is installed from the Mac App Store.
  • The file ~/.ssh/id_ed25519.pub contains the verified or newly generated public key used for personal-vpn.

Your Mac and server can now communicate securely through SSH. Next up, you will give both peers WireGuard keys and build the first encrypted tunnel.

Build the First Encrypted Tunnel

Your Mac can now administer the personal-vpn DigitalOcean Droplet through SSH. The next milestone is a private path that carries authenticated packets between two WireGuard peers.

This step builds a tunnel interface between the Mac and Droplet. The narrow 10.90.90.0/24 route gives you an early visible win before the Droplet handles the rest of your Mac's internet traffic.

In this step, get ready to:
  • Generate the gateway key pair securely.
  • Configure the Mac and Droplet as WireGuard peers.
  • Verify encrypted packet exchange through the private tunnel.

Why raw WireGuard?

Raw WireGuard exposes the keys, peer definitions, endpoints, private addresses, and routes that make the tunnel work. Tailscale automates several of these mechanisms through its coordination layer.

Configuring each part yourself makes the packet path visible. That visibility prepares you to diagnose the routing obstacle later.

Generate the gateway key pair

A private key proves the gateway's identity without leaving the Droplet. Its matching public key lets the Mac authenticate the gateway.

Key handling can feel sensitive. The gateway private key stays inside /etc/wireguard throughout this project.

  • In the SSH session from earlier, generate the gateway key pair securely by running these commands:
umask 077
wg genkey > gateway0.key
wg pubkey < gateway0.key > gateway0.pub
sudo mv gateway0.key gateway0.pub /etc/wireguard
cat /etc/wireguard/gateway0.pub

What do these commands do?

  • The umask 077 setting limits access to files created during this shell session.
  • The wg genkey > gateway0.key command creates the gateway private key.
  • The wg pubkey < gateway0.key > gateway0.pub command derives the matching public key.
  • The sudo mv gateway0.key gateway0.pub /etc/wireguard command stores both files beside the gateway configuration.
  • The final command prints the public key that the Mac can safely use.

You should see one line of key material. This is the public half of the gateway identity.

  • Store the displayed value here: your server public key.

No public key output?

  • Check that the SSH session is still connected to personal-vpn.
  • Confirm that each filename uses the gateway0 basename.
  • Run the key commands again after removing only the incomplete key files.
  • Help me troubleshoot the gateway WireGuard key generation.
Configure the two peers

The AllowedIPs setting controls which destination prefixes enter a tunnel. The 10.90.90.0/24 value keeps this first test limited to the private VPN subnet.

  • Press Cmd+Space to open macOS search.
  • Type WireGuard into the search field.
  • Press Enter to open WireGuard.
  • Create a tunnel from scratch.
  • Name the new tunnel personal-vpn.
  • Store the generated Mac public key here: your Mac public key.
  • Keep the generated PrivateKey value inside the tunnel editor.
  • Complete the personal-vpn profile by using this configuration as a reference:
[Interface]
PrivateKey = your-mac-private-key-here
Address = 10.90.90.2/24

[Peer]
PublicKey = your-server-public-key-here
Endpoint = your-droplet-public-ip-here:51000
AllowedIPs = 10.90.90.0/24
PersistentKeepalive = 25

How does the Mac profile work?

  • The Address line assigns 10.90.90.2/24 to the Mac side of the tunnel.
  • The peer PublicKey identifies the gateway that the Mac trusts.
  • The Endpoint line sends encrypted packets to the Droplet on UDP port 51000.
  • The AllowedIPs line routes only the private VPN subnet through this profile.
  • The PersistentKeepalive value sends an authenticated keepalive every 25 seconds.
  • Keep the app-generated private key in place of your-mac-private-key-here.
  • Insert your server public key in place of your-server-public-key-here.
  • Insert the Droplet's Public IPv4 address from earlier in place of your-droplet-public-ip-here.
  • Save the personal-vpn profile.

The WireGuard tunnel list now contains an inactive profile named personal-vpn.

Profile will not save?

  • Check that the configuration contains one [Interface] section.
  • Check that the configuration contains one [Peer] section.
  • Confirm that every placeholder has been replaced with its actual value.
  • Help me fix my WireGuard Mac profile.

The Mac now knows how to reach the gateway. The Droplet still needs a matching peer definition that trusts the Mac public key.

  • Return to the SSH session from earlier.
  • Create /etc/wireguard/gateway0.conf with Nano by running this command:
nano /etc/wireguard/gateway0.conf

What does this command do?

The nano command opens a terminal text editor. The path tells it to create the configuration for the gateway0 interface.

  • Paste this server configuration into Nano:
[Interface]
PostUp = wg set %i private-key /etc/wireguard/%i.key
Address = 10.90.90.1/24
ListenPort = 51000

[Peer]
PublicKey = your-mac-public-key-here
AllowedIPs = 10.90.90.2/32

How does the gateway configuration work?

  • The PostUp hook loads the matching private key after the interface starts.
  • The Address line assigns 10.90.90.1/24 to the Droplet side of the tunnel.
  • The ListenPort line accepts WireGuard packets on UDP port 51000.
  • The peer PublicKey identifies the Mac that the gateway trusts.
  • The peer AllowedIPs value binds that Mac peer to 10.90.90.2/32.
  • Replace your-mac-public-key-here with your Mac public key.
  • Press Ctrl+O to save the file.
  • Press Enter to confirm the filename.
  • Press Ctrl+X to close Nano.

The Droplet now has a gateway0 interface definition that trusts the Mac at 10.90.90.2/32.

Having trouble saving the server file?

  • Confirm that the Nano title shows /etc/wireguard/gateway0.conf.
  • Check that the Mac public key remains on a single line.
  • Make sure the section names include both square brackets.
  • Help me check my gateway0 configuration.

✔️ Awesome, I've got everything!

Both peer configurations now use the intended private addresses. Each side also holds the other peer's public key.

ⓧ I'd like to double check the full code

Compare the Mac profile with personal-vpn.conf. Keep the private key generated by the app.

[Interface]
PrivateKey = your-mac-private-key-here
Address = 10.90.90.2/24

[Peer]
PublicKey = your-server-public-key-here
Endpoint = your-droplet-public-ip-here:51000
AllowedIPs = 10.90.90.0/24
PersistentKeepalive = 25

Compare the Droplet configuration with /etc/wireguard/gateway0.conf.

[Interface]
PostUp = wg set %i private-key /etc/wireguard/%i.key
Address = 10.90.90.1/24
ListenPort = 51000

[Peer]
PublicKey = your-mac-public-key-here
AllowedIPs = 10.90.90.2/32
Prove the tunnel works

A successful handshake proves that both peers recognize each other's public keys. Transfer counters prove that encrypted packets are moving through the tunnel.

  • In the SSH session from earlier, start the server interface by running this command:
sudo wg-quick up gateway0

What does this command do?

The wg-quick command loads /etc/wireguard/gateway0.conf. It creates the gateway0 interface with the private address and listening port from that file.

You should see several setup lines for gateway0. The SSH prompt returns when the interface is ready.

Gateway interface did not start?

  • Check that /etc/wireguard/gateway0.key exists under the exact filename used by the PostUp hook.
  • Check that /etc/wireguard/gateway0.conf contains a real Mac public key.
  • Help me troubleshoot wg-quick for gateway0.
  • Activate the personal-vpn profile from the WireGuard tunnel list.
  • Approve the request if macOS asks to add the VPN configuration.
  • Return to Terminal without closing WireGuard.
  • Press Cmd+N to open a second Terminal window.

Before you test the private address, do you expect the Mac to receive replies from the Droplet?

  • Send packets through the private tunnel by running this command in the second Terminal window:
ping 10.90.90.1

What does this command prove?

The command sends packets to the Droplet's private tunnel address. Reply lines prove that the Mac can reach 10.90.90.1 through the authenticated peer connection.

You should see continuous reply lines from 10.90.90.1. Each reply is a packet that crossed the private tunnel.

  • Press Control-C to stop the ping.

No ping replies?

  • Confirm that the personal-vpn profile is active.
  • Confirm that the Mac peer uses the Droplet's current Public IPv4 address.
  • Check that the Cloud Firewall still permits UDP port 51000 from all IPv4 addresses.
  • Help me diagnose the private WireGuard ping.
  • Switch back to the SSH window from earlier.

Before you inspect the server, which two runtime signals would confirm that the Mac authenticated and exchanged packets?

  • Inspect the authenticated peer by running this command:
sudo wg show

What does the status show?

  • The interface section identifies gateway0 and its listening port.
  • The peer section identifies the Mac through its public key.
  • The latest handshake records the most recent successful peer authentication.
  • The transfer values count received and transmitted tunnel data.

That is your first encrypted tunnel working. You can see a latest handshake plus non-zero transfer values for the Mac peer.

Missing the handshake or transfer values?

  • Activate personal-vpn again if it became inactive.
  • Run the private ping again to send fresh tunnel traffic.
  • Compare each peer's public key with the value stored in the other peer's configuration.
  • Help me diagnose missing WireGuard runtime data.

Your Mac and Droplet can now exchange authenticated packets across 10.90.90.0/24. Next, you will send every IPv4 destination into the tunnel and inspect how far those packets get.

Break Internet Routing on Purpose

That private ping was your first networking win. Your Mac can exchange encrypted packets with the DigitalOcean Droplet.

A WireGuard handshake proves authenticated encryption between peers. This step tests whether a handshake also makes the Droplet a router for destinations beyond the tunnel.

In this step, get ready to:
  • Confirm the Mac's normal internet path.
  • Route every IPv4 destination through WireGuard.
  • Compare private tunnel access with public internet access.
Confirm your normal internet path

Your recorded public IPv4 address is the control value for normal internet access. Checking it with the tunnel off gives you a clean comparison.

  • Deactivate the active personal-vpn profile in WireGuard.
  • Use the Terminal menu bar to create a second window for local Mac commands.
  • Confirm the Mac uses your recorded baseline by running this command:
curl -4 ifconfig.me

What does this check prove?

  • The curl command asks ifconfig.me to report the public address used for this request.
  • The -4 flag limits the check to IPv4.
  • The returned address should match the baseline you recorded earlier.

No address returned?

  • Confirm that the personal-vpn profile is inactive.
  • Check that a familiar website loads over your normal connection.
  • Help me troubleshoot the baseline IPv4 check.
Route every IPv4 destination into WireGuard

The AllowedIPs field controls which destination ranges enter the tunnel. The default IPv4 route covers every possible IPv4 destination.

  • Use the edit control for the deactivated personal-vpn profile.
  • Add DNS = 1.1.1.1 directly below Address = 10.90.90.2/24.
  • Replace AllowedIPs = 10.90.90.0/24 with AllowedIPs = 0.0.0.0/0.
  • Save the updated profile.

✔️ Awesome, I've got everything!

Your saved profile now uses the default IPv4 route plus the specified DNS resolver.

ⓧ I'd like to double check the full code

  • Compare your saved personal-vpn.conf profile with this full configuration:
[Interface]
PrivateKey = your-mac-private-key-here
Address = 10.90.90.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = your-server-public-key-here
Endpoint = your-droplet-public-ip-here:51000
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

How does this profile choose routes?

  • The DNS = 1.1.1.1 setting uses the Cloudflare resolver for DNS lookups while this profile is active.
  • The AllowedIPs = 0.0.0.0/0 setting routes every IPv4 destination into the tunnel.
  • The PersistentKeepalive = 25 setting sends an authenticated keepalive every 25 seconds.
  • Reactivate the personal-vpn profile.

The active profile now claims the default IPv4 route.

Profile not staying active?

  • Check that the DNS line is inside the interface section.
  • Check that the new AllowedIPs value is inside the peer section.
  • Help me troubleshoot the full-tunnel profile.
Watch where the route stops

The full-tunnel route is active. The next request tests whether traffic can reach an internet destination through the Droplet.

Before you run this, which result do you expect: a public IPv4 address or a stalled request.

  • Test the new internet path from the local Terminal window by running this command:
curl -4 ifconfig.me

The routing gap is visible

No public IPv4 address returns. That shortfall is intentional.

WireGuard delivered packets to the Droplet. The Droplet lacks IPv4 forwarding plus source NAT for traffic leaving the tunnel.

  • Stop the stalled request after a short wait by pressing Control-C.

Before this check, choose your prediction: replies from 10.90.90.1 or silence.

  • Test the private tunnel address by running this command:
ping 10.90.90.1

What does the private ping prove?

You will see reply lines from 10.90.90.1. The encrypted peer-to-peer tunnel still works.

  • Stop the continuous ping replies by pressing Control-C.

Private ping not replying?

  • Confirm that only the personal-vpn profile is active.
  • Check that the destination is exactly 10.90.90.1.
  • Help me restore the private tunnel ping.
  • Switch back to the SSH session connected to personal-vpn.

Before the final check, predict whether the handshake timestamp plus transfer counters have changed.

  • Inspect the authenticated peer by running this command:
sudo wg show

What should you see?

You should see a latest handshake for the Mac peer. The transfer counters should be higher than before the full-tunnel tests.

These values prove that authenticated traffic reaches the Droplet even though general IPv4 internet access stops there.

No recent handshake?

  • Confirm that the Mac profile remains active.
  • Confirm that the server status lists the peer allowed at 10.90.90.2/32.
  • Help me troubleshoot the missing handshake.

You found the exact boundary. The encrypted tunnel works up to the Droplet.

You have separated a working encrypted tunnel from a working internet gateway. Next, you will give the Droplet the routing behavior that this test exposed as missing.

Turn the Droplet Into an Internet Gateway

Your encrypted WireGuard tunnel now carries packets from your Mac to the DigitalOcean Droplet. The private ping proves the peers authenticate. It also proves they can exchange traffic.

Internet access still fails because Linux does not forward packets between interfaces. The Droplet also needs source NAT to replace private tunnel addresses with its public address.

In this step, get ready to:
  • Confirm that the Droplet sends public traffic through eth0.
  • Enable IPv4 forwarding on Linux.
  • Add persistent source NAT for the VPN subnet.
Prepare Linux to route packets

The Droplet's default route identifies the network interface that carries traffic to the public internet. Your NAT rule must use that exact interface.

  • Inspect the Droplet's routes by running this command:
ip route show

What does this command show?

The default route is the path Linux uses for destinations outside its known networks. The interface name after dev identifies where public traffic leaves the Droplet.

You should find a default route that names eth0 after dev. This confirms the interface used by the project's NAT rule.

Does the route name another interface?

Stop before adding the NAT rule if the default route names an interface other than eth0. The target configuration for this Droplet expects eth0.

Ask for help with the route before continuing: help me understand why my DigitalOcean Droplet default route does not use eth0.

IPv4 forwarding lets Linux accept a packet on the WireGuard interface and send it through another interface. A persistent system setting keeps that routing behavior available after a reboot.

  • Use the terminal editor from earlier to create /etc/sysctl.d/70-wireguard-routing.conf with this exact content:
net.ipv4.ip_forward = 1

What does this setting do?

The net.ipv4.ip_forward setting controls whether Linux routes IPv4 packets between network interfaces. The value 1 enables that behavior.

  • Save /etc/sysctl.d/70-wireguard-routing.conf in the terminal editor.
  • Apply the forwarding setting by running this command:
sudo sysctl -p /etc/sysctl.d/70-wireguard-routing.conf -w

What does this command do?

The command reads the setting from /etc/sysctl.d/70-wireguard-routing.conf. Linux applies the value immediately.

You should see net.ipv4.ip_forward = 1 echoed in the terminal. The Droplet can now forward IPv4 packets between interfaces.

Is forwarding still disabled?

  • Check that /etc/sysctl.d/70-wireguard-routing.conf contains the setting exactly as shown.
  • Confirm that the filename ends in .conf.
  • Get help with the configuration: help me fix my IPv4 forwarding setting.
Connect the tunnel to the public internet

Packets from your Mac still carry the private source address 10.90.90.2. The public internet needs the Droplet to substitute its own public address before those packets leave through eth0.

  • Apply source NAT to the VPN subnet by running this command in the Droplet terminal:
sudo iptables -t nat -A POSTROUTING -s 10.90.90.0/24 -o eth0 -j MASQUERADE

How does masquerading work?

  • The POSTROUTING rule runs after Linux has chosen the packet's outbound route.
  • The source 10.90.90.0/24 limits the rule to your private VPN subnet.
  • The MASQUERADE target substitutes the Droplet's public IPv4 address for the private source address.
  • Switch to your web browser on the Mac.
  • Load a normal website while the personal-vpn profile remains active.

That is the routing gap closed. Your browser can now reach the internet through the Droplet.

Does the website still fail to load?

  • Confirm that the earlier forwarding command echoed net.ipv4.ip_forward = 1.
  • Check that the default route named eth0.
  • Ask for help with the gateway path: help me troubleshoot why my WireGuard client still has no internet after enabling forwarding and MASQUERADE.

The active NAT rule currently lives in memory. The compatibility script /etc/rc.local restores the same rule when Ubuntu starts.

  • Return to the SSH terminal from earlier.
  • Use the terminal editor from earlier to create /etc/rc.local with this exact content:
#!/bin/sh
iptables -t nat -A POSTROUTING -s 10.90.90.0/24 -o eth0 -j MASQUERADE

What does this file do?

The first line tells Ubuntu to run the file with the system shell. The second line restores the same source NAT rule during startup.

  • Save /etc/rc.local in the terminal editor.
  • Make the startup script executable by running this command:
sudo chmod +x /etc/rc.local

Why make the file executable?

Ubuntu includes /etc/rc.local in its startup behavior only when the file exists and has execute permission. This command adds that permission.

  • Confirm the file permissions by running this command:
ls -l /etc/rc.local

What should the permission check show?

The long listing displays the permission bits at the start of the line. You should see at least one x in those permission bits for /etc/rc.local.

Is the executable bit missing?

Run the permission command again if the listing has no x in its permission bits. Confirm that the path is exactly /etc/rc.local.

Get help with the file permission: help me make rc.local executable on Ubuntu.

Prove the gateway path

The final check compares the public address seen through the VPN with the address assigned to personal-vpn. Matching addresses prove that the Mac's IPv4 traffic exits through the Droplet.

  • Return to the Terminal app on your Mac.
  • Press Command-N to open a new local Terminal window.

Before you run the check, which public IPv4 address do you expect to see now?

  • Check the Mac's current public IPv4 address by running this command:
curl -4 ifconfig.me

What does this check prove?

The service returns the public IPv4 address used by your request. With the full-tunnel profile active, that request travels through the Droplet's forwarding and NAT path.

You should now see the Droplet's public IPv4 address. The result should differ from the Mac baseline address you recorded earlier.

  • Return to the personal-vpn Droplet detail page in the DigitalOcean Control Panel.
  • Compare the Terminal result with the Droplet's Public IPv4 address.

The two addresses should match. This proves websites now see the Droplet as the source of your IPv4 traffic.

  • Return to the SSH terminal from earlier.

Before you inspect the tunnel, do you expect its transfer totals to be higher after loading the website and checking your public address?

  • Inspect the authenticated peer by running this command:
sudo wg show

What should the peer status show?

The peer status includes its latest handshake plus received and sent transfer totals. Higher totals show that your browsing traffic crossed the encrypted tunnel.

You should see a recent handshake and increasing transfer counters. Your Droplet is now routing the Mac's IPv4 traffic to the internet.

Is the public address still wrong?

  • Confirm that the personal-vpn profile remains active in WireGuard.
  • Confirm that its AllowedIPs value remains 0.0.0.0/0.
  • Ask for help tracing the route: help me trace why my full-tunnel WireGuard profile does not show the Droplet public IPv4 address.

✔️ Awesome, I've got everything!

Great work. Your forwarding file and persistent NAT script now match the gateway configuration.

ⓧ I'd like to double check the full code

Compare these files with the versions on your Droplet and Mac. Keep every private key hidden while you check them.

[Interface]
PostUp = wg set %i private-key /etc/wireguard/%i.key
Address = 10.90.90.1/24
ListenPort = 51000

[Peer]
PublicKey = your-mac-public-key-here
AllowedIPs = 10.90.90.2/32

Gateway tunnel configuration

This unchanged server file defines the private tunnel address and authenticated Mac peer. Its interface name remains gateway0.

net.ipv4.ip_forward = 1

Forwarding configuration

This file enables IPv4 forwarding on the Droplet. It lets packets move from the WireGuard interface toward eth0.

#!/bin/sh
iptables -t nat -A POSTROUTING -s 10.90.90.0/24 -o eth0 -j MASQUERADE

Persistent NAT configuration

This startup script restores source NAT for 10.90.90.0/24 when Ubuntu boots.

[Interface]
PrivateKey = your-mac-private-key-here
Address = 10.90.90.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = your-server-public-key-here
Endpoint = your-droplet-public-ip-here:51000
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Full-tunnel Mac configuration

This profile sends every IPv4 destination through the WireGuard peer. The server now has the forwarding and NAT behavior needed to carry that traffic.

Your Mac can now browse through a VPN gateway you control. Next, you will reboot the Droplet and prove that its tunnel interface plus routing behavior return automatically.

Make the VPN Survive a Reboot

Your DigitalOcean Droplet now carries the Mac's IPv4 traffic through an authenticated WireGuard tunnel.

That working path currently depends on a live tunnel interface. A reboot can stop the interface unless Ubuntu starts it again.

You'll register gateway0 with Systemd. A real reboot will prove that the tunnel plus its routing behavior recover.

In this step, get ready to:
  • Enable the gateway0 WireGuard interface at boot.
  • Reboot personal-vpn and restore the SSH connection.
  • Prove that the private tunnel and public route recover.
Enable the interface at boot

The service name wg-quick@gateway0 points Systemd at the interface named gateway0. That interface name matches /etc/wireguard/gateway0.conf.

  • Register the existing gateway0 configuration with Systemd by running this command in the SSH session from earlier:
sudo systemctl enable wg-quick@gateway0

How Does Boot Registration Work?

  • The enable action records that wg-quick@gateway0 should start whenever Ubuntu boots.
  • The running interface stays available until you deactivate the Mac profile for the reboot test.

Good work. The VPN interface now has a boot instruction tied to its existing configuration.

Could Not Enable the Interface?

  • Confirm that /etc/wireguard/gateway0.conf exists on the Droplet.
  • Check that the service name ends with gateway0 to match the configuration filename.
  • Help me diagnose the boot service.
Reboot the Droplet and reconnect

Rebooting deliberately closes your current SSH session while Ubuntu restarts. Your files and Droplet settings remain in place during the test.

  • Switch back to the WireGuard app from earlier.
  • Deactivate the personal-vpn profile.
  • Return to the SSH session connected to personal-vpn.
  • Restart the Droplet by running this command:
sudo reboot

What Does This Restart Test?

This command restarts Ubuntu. The reboot tests whether the interface service plus your existing routing configuration return during startup.

Your SSH session closes as the reboot begins. Give the server about a minute to start before reconnecting.

  • Reconnect to the Droplet by running this command from your Mac:
ssh root@your-droplet-public-ip

What Does This Connection Prove?

This command starts a new encrypted administrative session using your existing SSH key. The Ubuntu welcome screen confirms that the Droplet is reachable after its reboot.

The server is back online. You can now test whether the VPN gateway recovered with it.

Unable to Reconnect?

  • Wait another minute before retrying because Ubuntu may still be starting.
  • Return to the DigitalOcean Control Panel to confirm that personal-vpn is running.
  • Confirm that the Cloud Firewall permits TCP port 22 from your recorded baseline public IPv4 address.
  • Help me restore SSH access.
Verify the recovered VPN path

Boot recovery has two layers. The private ping proves that the WireGuard interface returned.

The public IPv4 check proves that forwarding plus source NAT returned.

  • Switch back to the WireGuard app from earlier.
  • Reactivate the personal-vpn profile.
  • Create a second Terminal window for the Mac-side checks.

Before you run the first recovery check, do you think the private tunnel address will reply after a full server reboot?

  • Test the private tunnel address by running this command in the local Terminal window:
ping 10.90.90.1

What Does the Private Ping Test?

This command sends packets to the Droplet's private tunnel address. Replies prove that gateway0 started after the reboot.

You'll see continuous replies from 10.90.90.1. The authenticated private path is working again.

  • Stop the continuous ping by pressing Control-C.

Now predict which public IPv4 address the Mac will use while the recovered full-tunnel profile is active.

  • Check the public IPv4 route by running this command:
curl -4 ifconfig.me

What Does the Public Address Confirm?

This command asks ifconfig.me which public IPv4 address received the request. A match with the Droplet's public IPv4 address proves that forwarding plus source NAT recovered.

You'll see the public IPv4 address shown for the Droplet in the DigitalOcean Control Panel. Your Mac is reaching the internet through personal-vpn again.

  • Return to the SSH window connected to the Droplet.

Before the final server check, what would prove that the Mac created a new peer session after the reboot?

  • Inspect the recovered peer session by running this command:
sudo wg show

What Does the Peer Status Confirm?

This command displays the running WireGuard interface plus its authenticated peer. A fresh latest handshake proves that the Mac reconnected after the reboot.

The transfer counters show how much traffic has crossed the restored tunnel. Their values increase as you use the VPN.

That's the reliability test complete. Your personal VPN now recovers after an Ubuntu reboot.

Did a Recovery Check Fail?

  • Confirm that the personal-vpn profile is active before retrying the private ping.
  • Check whether the public IPv4 result matches the Droplet instead of the saved Mac baseline.
  • Confirm that the peer status displays gateway0 with a recent handshake.
  • Help me diagnose the failed recovery check.

✔️ Awesome, I've got everything!

Great work. The private ping plus the public IPv4 result prove that the gateway recovered after the reboot.

ⓧ I'd like to double check the full code

Compare these files with the configurations on your Droplet plus the full-tunnel profile on your Mac. This step enables the boot service without changing their contents.

[Interface]
PostUp = wg set %i private-key /etc/wireguard/%i.key
Address = 10.90.90.1/24
ListenPort = 51000

[Peer]
PublicKey = your-mac-public-key-here
AllowedIPs = 10.90.90.2/32
net.ipv4.ip_forward = 1
#!/bin/sh
iptables -t nat -A POSTROUTING -s 10.90.90.0/24 -o eth0 -j MASQUERADE
[Interface]
PrivateKey = your-mac-private-key-here
Address = 10.90.90.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = your-server-public-key-here
Endpoint = your-droplet-public-ip-here:51000
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Secret mission

Add a Split-Tunnel Profile

Add a second WireGuard profile that sends only private VPN traffic through your Droplet. You will reach the gateway while your normal internet traffic keeps using the Mac's local connection.

Clean Up Your Resources

Clean Up Your Resources

Your DigitalOcean Droplet stays billable for as long as it exists. The Basic 512 MiB plan costs up to $4 per month.

Choose whether to keep your VPN gateway available, pause your use of it, or delete its resources entirely.

Cost warning

The Basic 512 MiB Droplet includes 500 GiB of monthly transfer. Extra outbound transfer costs $0.01 per GiB.

Powering off the Droplet does not stop compute billing. Destroying the Droplet ends those compute charges.

Resources you used:

  • One Basic 512 MiB Droplet named personal-vpn containing the server-side VPN gateway configuration.
  • One DigitalOcean Cloud Firewall protecting personal-vpn.
  • Two WireGuard profiles named personal-vpn and personal-vpn-split.

Keep everything running

No action needed. Choose this if you want your personal VPN gateway to remain available for future connections.

  • Leave the personal-vpn Droplet running.
  • Keep the Cloud Firewall attached to personal-vpn.
  • Keep both WireGuard profiles on your Mac.
  • Monitor the Droplet's transfer usage in the DigitalOcean Control Panel.
  • Review your DigitalOcean billing while the Droplet exists.

Pause - I'll come back to this later

This option disconnects your Mac from the VPN while preserving the gateway for later. The Droplet continues to incur charges during the pause.

  • In WireGuard for macOS, deactivate personal-vpn-split.
  • Confirm that personal-vpn remains inactive.
  • Leave the personal-vpn Droplet running because powering it off does not stop billing.
  • Keep the Cloud Firewall attached to the Droplet.
  • Reactivate the profile you need when you return to the project.

Delete - I don't want to use this again

Deleting the Droplet permanently removes its server-side keys and configuration. That clean break is safe once you no longer need this VPN gateway.

  • In the DigitalOcean Control Panel, select the personal-vpn Droplet to reach its detail page.
  • Select Settings.
  • Choose Destroy.
  • Enter personal-vpn when prompted.
  • Confirm the permanent deletion.

When personal-vpn no longer appears in your Droplets, the paid compute resource is gone. Its compute billing has ended.

  • Return to Networking in the DigitalOcean Control Panel.
  • Select Firewalls.
  • Open the Cloud Firewall that was applied to personal-vpn.
  • Use the firewall's deletion action.
  • Confirm the firewall deletion.

When the firewall no longer appears in Firewalls, its stateful network rules have been removed.

  • In WireGuard for macOS, deactivate personal-vpn-split if it still shows as active.
  • Select personal-vpn in the app.
  • Remove the selected profile with the app's profile removal control.
  • Select personal-vpn-split in the app.
  • Remove the selected profile with the app's profile removal control.
  • Leave your local SSH key files in place because this project may have reused an existing key.

You should now see neither personal-vpn nor personal-vpn-split in the WireGuard app.

Nice Work!

Nice Work!

You did it! Your Mac now routes IPv4 traffic through an authenticated WireGuard gateway on a DigitalOcean Droplet.

You've learned how to:

  • Built an authenticated tunnel between your Mac and an Ubuntu server. Proved peer connectivity with replies from 10.90.90.1. Read the latest handshake to confirm peer authentication. Watched the transfer counters rise to confirm packet flow.
  • Separated a working tunnel from a working routed VPN gateway. Enabled IPv4 forwarding on the Droplet. Applied source NAT to send private VPN traffic through eth0. Proved the repaired path when curl -4 ifconfig.me returned the Droplet's public IPv4 address.
  • Protected the server with a stateful DigitalOcean Cloud Firewall. Made gateway0 start after an Ubuntu reboot. Restored the NAT rule during the same boot. Confirmed the recovered gateway with a fresh handshake.
  • Secret Mission: Added a second WireGuard peer for split-tunnel routing. Routed 10.90.90.0/24 through the VPN. Kept normal internet traffic on the Mac's local connection.

Ready to quiz yourself?