Traveling but Still at Home: My VPN Setup with wg-easy and Cloudflare

A few weeks ago, I was packing for a family vacation abroad. Like most people, I wasn’t planning to take my entire home network with me, but I did want to keep access to a few things:

  • My home server with family photos.
  • Streaming services (Netflix, local TV provider) that only work in my country
  • My smart home dashboard

The challenge? My ISP gives me a dynamic IP, my router blocks incoming traffic unless I open ports, and I definitely didn’t want to leave my network UI exposed to the internet.

What You’ll Build

  • A WireGuard VPN managed by wg-easy
  • Cloudflare Tunnel to safely access wg-easy’s web UI (no port 51821 exposure)
  • Port forwarding for UDP/51820 on your router for VPN traffic
  • Dynamic DNS updates to Cloudflare via cloudflare-ddns so your clients always find you

Prerequisites

  • A Linux server (Ubuntu 22.04/24.04 recommended)
  • Docker + Docker Compose installed
  • A Cloudflare account with a domain
  • Access to your router for port forwarding

Step 1: Deploy wg-easy

I’m using Docker Compose to deploy the wg-easy instance using the following file:

volumes:
  etc_wireguard:

services:
  wg-easy:
    environment:
      - WG_HOST=dhd.klein.org.il          # your public DNS name / static IP
      - WG_ALLOWED_IPS=0.0.0.0/    # ← split-tunnel to your home LAN
      - WG_DEFAULT_DNS=192.168.0.231,1.1.1.1
      - WG_PERSISTENT_KEEPALIVE=25

    image: ghcr.io/wg-easy/wg-easy:15
    container_name: wg-easy
    networks:
      wg:
        ipv4_address: 10.42.42.42
        ipv6_address: fdcc:ad94:bacf:61a3::2a
    volumes:
      - ./wireguard:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
      # - NET_RAW # ⚠️ Uncomment if using Podman
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv6.conf.all.forwarding=1
      - net.ipv6.conf.default.forwarding=1

networks:
  wg:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 10.42.42.0/24
        - subnet: fdcc:ad94:bacf:61a3::/64
  • WG_ALLOWED_IPS: Set to 0.0.0.0 if you want to access external IPs (WAN).
  • 51820/UDP → must be forwarded on your router to the Docker host.
  • No need to expose 51821/TCP (UI) — Cloudflare Tunnel will handle that.

Step 2: Secure the Web UI with Cloudflare Tunnel

I‘m using Cloudflare tunnel (Cloudflared) to secure access to the wg-easy UI. Why Cloudflare Tunnel? because it is the best way to allow access to the UI without exposing the port in your router, and also allows us to set better access control, like geo-blocking, WAF, and more.

Open you docker-compose file and add the following text into it:

  cloudflared:
    image: cloudflare/cloudflared:latest # or a specific version like 2025.8.22
    restart: unless-stopped
    command: tunnel run
    environment:
      - TUNNEL_TOKEN=${TUNNEL_TOKEN} # Recommended for dashboard-managed tunnels
  • TUNNEL_TOKEN= The token of the tunnel you created in Cloudflare.

Then configure a tunnel that maps a DNS record (e.g., vpn-ui.example.com) → http://wg-easy:51821.

Advantages:

  • No extra firewall ports open
  • Protected by Cloudflare Access policies if desired (SSO, 2FA)

Step 3: Dynamic DNS with cloudflare-ddns

When your ISP gives you a changing IP, you can keep WG_HOST updated automatically.

Example docker-compose:

  cloudflare-ddns:
    image: oznu/cloudflare-ddns
    environment:
      - API_KEY=${CF_API_KEY}
      - ZONE=example.com
      - SUBDOMAIN=vpn
    restart: unless-stopped

Step 4: Router Configuration

To allow the client to access and connect to the VPN server, we need to expose (Port forwarding) the VPN UDP port:

  • Forward UDP 51820 → Docker host
  • Do not forward 51821 (UI is only accessible via Cloudflare Tunnel)

Step 5: Configure wg-easy

To start working with wg-easy, run the following command:

docker-compose up -d

This will start all the services we just configured.

Next, open your browser and navigate to the URL you assigned to the wg-easy UI. On the welcome screen, click on the “Continue” button:

Next, create the admin account:

If this is a clean installation, on the next screen, click on the “No” button:

Next, enter the domain selected for the VPN server (not the wg-easy ui) and click on “Continue”:

Next, click on “Sign In” to access the server UI:

Step 6: Create client configuration

On the main screen of the wg-easy, click on the “New client” button:

Set the name of the profile you would like to create and set the expiration date (Optional but highly recomended):

Back to the profiles (clients) list:

The icons (left to right):

  • Edit: Allows you to edit the profile and change the allowed IPs, set the client IP, change the name, or the expiration date.
  • QR: By scanning the QR, you can configure the client.
  • Download: download the configuration file and import it on the client.
  • Create a short-lived, one-time link to the configuration.

Step 7: Securing the UI with 2FA

As always, whenever it is possible, enable 2FA to secure your application.

To do so, click on “Administrator” in the upper right corner and then on “Account.”

Scroll down to the bottom of the page and click on “Enable Two Factor Authentication.”

Scan the QR code, enter the six-digit OPT, and click on the “Enable Two Factor Authentication” button.

To disable the 2FA, enter your password and click the “Disable Two Factor Authentication.” button:

Wrap Up

Traveling doesn’t mean losing access to your home network or streaming services. With a few Docker containers and Cloudflare, I now have a vacation-proof VPN that “brings home with me” wherever I go.

So next time I’m at a café in Rome or a hotel in Tokyo, I just tap my WireGuard app — and suddenly I’m home.

Be the first to comment

Leave a Reply

Your email address will not be published.


*