Access your home server from anywhere with WireGuard. Docker Compose setup with wg-easy, mobile client config, split tunneling, and Pi-hole ad blocking on the go.
WireGuard has become the default choice for self-hosters who want reliable, fast remote access to their home server without the complexity overhead of legacy VPN protocols. If you have been running your home server for a while and want to reach it securely from anywhere โ a hotel network, a coffee shop, or your phone's cellular connection โ this guide walks through a complete WireGuard setup using Docker, including split tunneling, DNS ad-blocking integration, and real-world performance numbers on the Intel N100 hardware that dominates the home server space in 2026.
This guide assumes you have a Linux home server already running Docker. If you are just getting started, the home server beginner's guide for 2026 covers the foundational setup before you add networking complexity.

OpenVPN has been the go-to self-hosted VPN for over a decade, but it carries significant baggage. The OpenVPN codebase runs to roughly 100,000 lines. WireGuard accomplishes the same core function in under 4,000 lines โ which matters because every line of code is a potential attack surface. The smaller codebase has been formally verified and reviewed by cryptographers in a way that OpenVPN's codebase simply cannot be at that scale.
Beyond security, the practical advantages stack up quickly:
Speed. WireGuard operates inside the Linux kernel (or via a fast userspace implementation on other platforms). Handshake time is under 100 milliseconds. Reconnection after a network change is nearly instantaneous โ critical for mobile clients that hop between Wi-Fi and cellular.
Modern cryptography. WireGuard uses ChaCha20 for symmetric encryption, Poly1305 for authentication, Curve25519 for key exchange, BLAKE2 for hashing, and SipHash24 for hashtable keys. There are no cipher negotiation vulnerabilities because there is nothing to negotiate โ the cryptographic suite is fixed and current.
Low CPU overhead. On an Intel N100 processor running at around 6 watts idle, WireGuard imposes less than 5% additional CPU load during active transfers. OpenVPN on the same hardware can push CPU utilization into the 30โ40% range for a single client streaming large files.
Battery-friendly on mobile. Because WireGuard only sends packets when there is actual traffic (no keepalive polling), it is significantly easier on mobile battery life than OpenVPN.
The primary trade-off is that WireGuard requires more manual configuration than managed alternatives like Tailscale. If you want to understand and fully own your network setup, that trade-off is worthwhile.

Before committing to WireGuard, it is worth understanding where each option fits. For a deeper comparison that covers additional options like ZeroTier and Headscale, see the secure remote access comparison.
| Feature | WireGuard (self-hosted) | Tailscale | OpenVPN (self-hosted) |
|---|---|---|---|
| Setup complexity | Medium | Very low | High |
| Full infrastructure control | Yes | Partial (control plane with Tailscale) | Yes |
| NAT traversal | Manual (port forward) | Automatic | Manual |
| Performance | Excellent | Excellent (uses WireGuard underneath) | Good |
| Mobile battery impact | Low | Low | High |
| Free tier limits | None (self-hosted) | 3 users, 100 devices | None (self-hosted) |
| Codebase size | ~4,000 lines | Proprietary control plane | ~100,000 lines |
| Custom DNS routing | Manual | Built-in | Manual |
| Requires inbound port | Yes (51820 UDP) | No | Yes (1194 UDP/TCP) |
| Audit log / access control | Manual | Built-in | Plugin-based |
Use WireGuard when you want complete control over your VPN infrastructure, have no objection to configuring port forwarding and dynamic DNS, and want zero ongoing dependency on a third-party service.
Use Tailscale when you want a working VPN in ten minutes, are behind strict CGNAT (where port forwarding is impossible), or need to give several family members access without maintaining client configs manually.
Use OpenVPN when you need compatibility with legacy corporate network configurations or specific compliance requirements that reference OpenVPN explicitly.
For most home server users reading this guide, WireGuard with wg-easy is the right answer.

Before starting, confirm you have the following in place:
192.168.1.10)https://ifconfig.me)Your server does not need a powerful CPU for this. WireGuard runs efficiently on anything from a Raspberry Pi 4 to an N100 mini PC. If you are building out a new home server setup, the home server beginner's guide for 2026 covers hardware selection.
The easiest production-ready method is to use wg-easy, a containerized WireGuard server with a web UI for managing clients. This eliminates manual wg0.conf editing and gives you a clean interface for adding or revoking client access.
mkdir -p /opt/wg-easy
cd /opt/wg-easy
Create the docker-compose.yml file with your specific settings (full file in the next section).
Start the container:
docker compose up -d
Log in to your router's admin panel and add a port forwarding rule:
51820UDP192.168.1.10)51820Navigate to the wg-easy web UI in your browser:
http://192.168.1.10:51821
Log in with the password you set in your WG_DEFAULT_PASSWORD environment variable and create your first client configuration by clicking "New Client" and entering a name (e.g., "phone" or "laptop").
Download the generated WireGuard configuration file or scan the QR code with the WireGuard mobile app.
Install the WireGuard client on your phone or laptop:
sudo apt install wireguardImport the downloaded .conf file (or scan the QR code) and activate the tunnel.
Verify the connection by checking your public IP from a browser while connected โ it should resolve to your home IP address. You can also ping your server's internal IP (192.168.1.10) to confirm tunnel connectivity.
Here is the complete docker-compose.yml for wg-easy with annotations explaining each variable:
version: "3.8"
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:latest
container_name: wg-easy
restart: unless-stopped
ports:
# WireGuard VPN port โ must match your router's port forward rule
- "51820:51820/udp"
# Web UI port โ accessible only from your local network
- "51821:51821/tcp"
volumes:
# Persistent storage for WireGuard config and client keys
- ./config:/etc/wireguard
cap_add:
# Required for creating the wg0 network interface
- NET_ADMIN
- SYS_MODULE
sysctls:
# Enable IP forwarding so VPN clients can route through the server
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
environment:
# Your home server's public IP or dynamic DNS hostname
# If using DuckDNS: myhostname.duckdns.org
# If using static IP: your actual public IP address
- WG_HOST=your-hostname.duckdns.org
# Web UI password โ change this to something strong
- WG_DEFAULT_PASSWORD=change-me-now
# WireGuard listen port (must match the ports mapping above)
- WG_PORT=51820
# Internal VPN subnet โ clients will get IPs from this range
# 10.8.0.0/24 supports up to 253 clients
- WG_DEFAULT_ADDRESS=10.8.0.x
# DNS server clients will use while connected
# Option A: Use your Pi-hole/AdGuard Home for ad blocking over VPN
# - WG_DEFAULT_DNS=192.168.1.20
# Option B: Use Cloudflare for straightforward DNS
- WG_DEFAULT_DNS=1.1.1.1,1.0.0.1
# Web UI port
- PORT=51821
# Maximum data transferred per client before UI shows a warning
# Set to 0 to disable the limit
- WG_ALLOWED_IPS=0.0.0.0/0,::/0
# Enable persistent keepalive (prevents NAT timeout on strict firewalls)
- WG_PERSISTENT_KEEPALIVE=25
After editing this file with your values, bring up the container:
docker compose up -d
docker compose logs -f wg-easy
The logs should show WireGuard starting and the web UI listening on port 51821. The ./config directory will be populated with wg0.conf and client configuration files.
Most residential ISPs reassign your public IP address periodically. If your IP changes while you are away, WireGuard clients will lose connectivity. Dynamic DNS maps a stable hostname to your changing IP.
DuckDNS is a free option with Docker-based auto-update:
# Add this service to your existing docker-compose.yml
duckdns:
image: lscr.io/linuxserver/duckdns:latest
container_name: duckdns
restart: unless-stopped
environment:
# Your DuckDNS subdomain (without .duckdns.org)
- SUBDOMAINS=your-subdomain
# Your DuckDNS token from the account dashboard
- TOKEN=your-duckdns-token
# Update frequency in seconds
- TZ=America/New_York
volumes:
- ./duckdns:/config
Once this is running, set WG_HOST=your-subdomain.duckdns.org in the wg-easy environment block. DuckDNS updates the DNS record automatically whenever your public IP changes, and WireGuard clients use the hostname rather than a hard-coded IP.
Cloudflare is an alternative if you already own a domain. Update your A record via the Cloudflare API on a cron schedule, or use a tool like ddclient configured for Cloudflare.
By default, the WG_ALLOWED_IPS=0.0.0.0/0,::/0 setting routes all client traffic through your home server โ a "full tunnel." This means all your internet traffic exits through your home connection while on VPN, which is useful for privacy but wastes bandwidth if you just want to reach your home services.
Split tunneling routes only traffic destined for your home network through the VPN. Internet traffic continues to exit locally on the client.
To enable split tunneling, change the WG_ALLOWED_IPS variable in your docker-compose.yml:
# Replace the full tunnel setting:
# - WG_ALLOWED_IPS=0.0.0.0/0,::/0
# With your home network CIDR:
- WG_ALLOWED_IPS=192.168.1.0/24,10.8.0.0/24
The first CIDR (192.168.1.0/24) allows access to your home LAN devices. The second (10.8.0.0/24) allows VPN clients to reach each other through the tunnel.
After changing this setting, restart the container and re-download the client configuration from the web UI. The new AllowedIPs value will be embedded in the client's .conf file.
The trade-off: with split tunneling and a local DNS setting pointing to Pi-hole, you may not get ad blocking for all traffic โ only for traffic routed through the tunnel. If ad blocking everywhere matters to you, use the full tunnel configuration.
One of the most useful WireGuard combinations is routing your VPN DNS through your home Pi-hole or AdGuard Home instance. This extends your ad-blocking coverage to all your devices regardless of network โ hotel Wi-Fi, corporate networks, mobile data.
To configure this, change the DNS setting in docker-compose.yml to point to your Pi-hole server's internal IP:
# Replace Cloudflare DNS:
# - WG_DEFAULT_DNS=1.1.1.1,1.0.0.1
# With your Pi-hole internal IP:
- WG_DEFAULT_DNS=192.168.1.20
Where 192.168.1.20 is the static IP address of your Pi-hole server. Restart wg-easy and regenerate client configs.
Important: confirm that your Pi-hole is configured to accept DNS queries from the WireGuard subnet (10.8.0.0/24). In the Pi-hole admin panel under Settings > DNS, check "Listen on all interfaces" or add the WireGuard interface specifically. This is the most common reason this setup fails.
If you are using Pi-hole in Docker on the same host, make sure both containers share a Docker network or that Pi-hole is bound to 0.0.0.0 rather than only 127.0.0.1.
For a detailed guide on hardening your server's overall security posture beyond DNS-level blocking, the home server security hardening guide covers firewall rules, SSH key management, and fail2ban configuration that complement a VPN setup.
Once WireGuard is running, you access your home services by their internal IP address or hostname, exactly as you would on your local network. No additional configuration is required for individual services โ the VPN makes your remote device look like it is on your home LAN.
Example access patterns:
# Access Nextcloud (running on port 8080)
http://192.168.1.10:8080
# Access Jellyfin (running on port 8096)
http://192.168.1.10:8096
# Access your router admin panel
http://192.168.1.1
# Access Pi-hole dashboard
http://192.168.1.20/admin
If you have set up a local DNS server (Pi-hole, AdGuard Home, or a custom /etc/hosts equivalent), you can use hostnames:
# Access Nextcloud via local hostname
http://nextcloud.home
# Access Jellyfin via local hostname
http://jellyfin.home
For Nextcloud specifically, there is one extra step: Nextcloud validates requests against a whitelist of trusted domains and proxies. When connecting over WireGuard, your request originates from a 10.8.0.x IP. Add the WireGuard subnet to your Nextcloud trusted proxies. See the Nextcloud Docker Compose setup guide for the specific config.php entries.
For Jellyfin, no extra configuration is needed โ connecting from a WireGuard IP is treated identically to a LAN connection, giving you full streaming quality without the bandwidth overhead of Jellyfin's external relay. This means you get your full local network speed (typically 1 Gbps) rather than whatever your upstream internet bandwidth allows. Full details on media server options are in the Jellyfin vs Plex vs Emby comparison.
The Intel N100 is the dominant home server CPU in 2026: four efficient cores, 6W TDP, and AES-NI hardware acceleration built in. WireGuard benefits directly from AES-NI through its ChaCha20/Poly1305 cipher suite, which is actually optimized for hardware without AES acceleration โ but the N100's vector units still provide meaningful throughput gains.
Practical measurements from community testing on N100-based mini PCs:
| Scenario | WireGuard throughput | CPU utilization | OpenVPN throughput | OpenVPN CPU |
|---|---|---|---|---|
| Single client, large file transfer | 450โ500 Mbps | 8โ12% | 180โ220 Mbps | 35โ45% |
| Single client, Jellyfin 4K stream | 40โ60 Mbps | 2โ3% | 40โ60 Mbps | 15โ20% |
| Three concurrent clients | 380โ420 Mbps aggregate | 18โ25% | 120โ160 Mbps aggregate | 65โ80% |
| Idle (no active traffic) | 0 Mbps | <1% | 0 Mbps | 1โ3% |
For home server use cases โ streaming media, syncing files with Nextcloud, accessing dashboards โ WireGuard's overhead is genuinely negligible on N100 hardware. The bottleneck in practice is almost always your home internet upload speed, not the server's VPN processing capacity.
WireGuard's kernel integration means it avoids user-space context switching overhead. On the N100, this translates to roughly 3โ4x better throughput per core compared to OpenVPN under load.
Clients cannot connect at all
Start with the basics: confirm port 51820 UDP is open in your router's port forwarding rules. Test from outside your network (use your phone on cellular, not Wi-Fi). Run:
# On the server โ check that WireGuard is listening
docker compose exec wg-easy wg show
# On the server โ verify the container is up
docker compose ps
# Check for firewall rules blocking the port on the server itself
sudo ufw status
sudo iptables -L INPUT -n
If ufw is active, allow the WireGuard port:
sudo ufw allow 51820/udp
Connected but cannot reach internal services
This usually means IP forwarding is not enabled, or the NET_ADMIN capability is missing from the container. Verify:
# Should return 1
cat /proc/sys/net/ipv4/ip_forward
# If 0, enable it persistently
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf
Also check that AllowedIPs in the client config includes your home subnet. If you are using split tunneling, the home subnet CIDR must be explicitly listed.
DNS leaks or DNS not resolving over VPN
Use a DNS leak test site (dnsleaktest.com) while connected to WireGuard. If the results show your ISP's DNS servers instead of your configured DNS, the client's DNS override is not applying correctly.
On Linux clients, check that resolvconf or systemd-resolved is integrated with WireGuard:
# Check the PostUp/PostDown hooks in the client wg0.conf
grep -i dns /etc/wireguard/wg0.conf
# For systemd-resolved systems, verify DNS assignment
resolvectl status
On macOS and iOS, DNS settings from the WireGuard config are applied automatically โ no manual intervention needed.
Slow speeds despite WireGuard being active
Check MTU fragmentation. The default WireGuard MTU of 1420 bytes works for most connections, but some ISPs (particularly those using PPPoE) require lower values. If speeds are slow but connection is stable, try:
# In the client's [Interface] section, add:
MTU = 1380
Also verify that your home internet upload bandwidth is not the limiting factor. WireGuard cannot exceed your ISP's upstream capacity.
Connection drops when switching networks (mobile)
WireGuard handles network changes gracefully in most cases, but if you see persistent drops, enable PersistentKeepalive in the client config. This is already set in the wg-easy configuration via WG_PERSISTENT_KEEPALIVE=25, but you can also add it manually to existing client configs:
[Peer]
PublicKey = <server-public-key>
Endpoint = your-hostname.duckdns.org:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
For the majority of home server use cases in 2026, yes. WireGuard offers 2โ4x better throughput on the same hardware, significantly lower CPU overhead, faster reconnection times (sub-100ms vs several seconds for OpenVPN), and a codebase that is small enough to have been formally verified. The cryptographic suite is modern and fixed โ there is no cipher negotiation that can be downgraded or misconfigured.
OpenVPN's remaining advantages are compatibility and flexibility. It supports TCP transport (useful on networks that block UDP), has been deployed in enterprise environments for over 20 years, and has more extensive plugin support for features like LDAP authentication. If you are connecting to a corporate VPN that you do not control, you may not have a choice. For a VPN server you are running yourself, WireGuard is the better choice on nearly every practical metric.
Once WireGuard is running on your home server and your router has port 51820 UDP forwarded to the server, you access your home network from anywhere by activating the WireGuard tunnel on your client device (phone, laptop, etc.) and then using your devices' normal internal IP addresses or hostnames.
The critical requirement is that your server's public IP (or a dynamic DNS hostname pointing to it) is reachable from the internet. Hotel networks, cellular connections, and most corporate networks allow outbound UDP traffic, so WireGuard connections work without any special configuration on the client side.
If your ISP uses CGNAT (where your home does not have a real public IP), port forwarding will not work. In that scenario, consider Tailscale (which performs DERP relay for NAT traversal) or a cheap VPS as a WireGuard relay point using a configuration called a "bouncer" or "relay server."
Yes โ the wg-easy container used in this guide is the recommended approach for most home servers. Running WireGuard in Docker simplifies updates, keeps configuration portable, and makes it easy to manage client keys through a web UI rather than manually editing configuration files.
There are two technical requirements for containerized WireGuard: the container needs NET_ADMIN and SYS_MODULE Linux capabilities to create and manage the wg0 network interface, and IP forwarding must be enabled on the host system. The docker-compose.yml provided in this guide includes both.
The alternative is installing WireGuard directly on the host with apt install wireguard. This is slightly more performant (no container network overhead) and is appropriate if you prefer managing configuration through systemd rather than Docker. For most users, the wg-easy Docker approach is easier to maintain.
The short answer is: use WireGuard if you want full control and do not mind some configuration; use Tailscale if you want it working in ten minutes with no router changes.
Tailscale uses WireGuard as its underlying protocol but wraps it with a managed control plane. Tailscale handles key distribution, NAT traversal, device authentication, and access control policies automatically. The trade-off is that your device list and authentication flow run through Tailscale's servers โ you are trusting them with your network topology metadata, though not your traffic content.
WireGuard self-hosted means your control plane is your own wg0.conf on your server. Nothing leaves your infrastructure. You control exactly who has access and can revoke it instantly. The cost is that you need to handle key distribution yourself (wg-easy makes this manageable), configure port forwarding, and set up dynamic DNS.
If you have family members who need access with minimal friction, Tailscale's client apps are simpler. If you are a solo operator who values infrastructure ownership and has already done the work described in this guide, self-hosted WireGuard is the more capable long-term solution.
WireGuard with wg-easy gives you a production-ready VPN server in under 30 minutes, with a web interface for managing clients, solid performance on low-power hardware, and complete control over your network. The combination of split tunneling, Pi-hole DNS routing, and Docker portability makes it one of the most versatile additions to a home server setup.
The configuration shown here โ Docker Compose with persistent volumes, dynamic DNS, and the wg-easy web UI โ is suitable for long-term production use. Add clients as you need them, revoke access with a single click, and update the container with docker compose pull && docker compose up -d when new versions ship.
Once your VPN is running, the next logical steps are hardening the rest of your server's security posture (see the security hardening guide) and ensuring the self-hosted services you are accessing remotely are properly configured for external access, starting with Nextcloud and Jellyfin.

Use Cases
Compare Tailscale, WireGuard, and Cloudflare Tunnels for secure home server access, including setup steps, security differences, and best-fit use cases.

Optimization
Compare Tailscale and Cloudflare Tunnel for home server access. Setup guides, security analysis, and best practices for 2025.
Use Cases
Automate SSL renewal, backups, Docker updates & health alerts with cron, n8n workflows, Watchtower, and Ansible playbooks. Resource usage benchmarks on N100.
Check out our build guides to get started with hardware.
View Build Guides