How to Expose Self-Hosted Services to the Internet
What Is This Guide?
You’ve set up self-hosted services at home. Now you want to access them from outside your network — from your phone, your office, or anywhere on the internet. This guide covers every method for exposing self-hosted services, ranked by security, complexity, and cost.
Prerequisites
- A running self-hosted service you want to access remotely
- A domain name (recommended but not required for all methods)
- Basic understanding of networking concepts
- A reverse proxy (for public-facing methods)
The Four Approaches
| Method | Security | Complexity | Cost | Best For |
|---|---|---|---|---|
| Cloudflare Tunnel | High | Low | Free | Public services (websites, APIs) |
| VPN (Tailscale/WireGuard) | Very High | Medium | Free | Personal access to all services |
| Port Forwarding + Reverse Proxy | Medium | Medium | Free | Full control, no third parties |
| VPS Reverse Proxy | High | High | $5-12/mo | Static IP + privacy |
Method 1: Cloudflare Tunnel (Recommended for Public Services)
Cloudflare Tunnel creates an outbound-only connection from your server to Cloudflare’s network. No open ports, no port forwarding, no exposed IP address. Traffic flows: Internet → Cloudflare → Tunnel → Your server.
Setup
- Add your domain to Cloudflare (free plan works)
- Install
cloudflaredon your server:
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
- Authenticate and create a tunnel:
cloudflared tunnel login
cloudflared tunnel create my-tunnel
- Configure the tunnel (
~/.cloudflared/config.yml):
tunnel: <tunnel-id>
credentials-file: /root/.cloudflared/<tunnel-id>.json
ingress:
- hostname: nextcloud.yourdomain.com
service: http://localhost:8080
- hostname: jellyfin.yourdomain.com
service: http://localhost:8096
- service: http_status:404
- Route DNS and run:
cloudflared tunnel route dns my-tunnel nextcloud.yourdomain.com
cloudflared tunnel route dns my-tunnel jellyfin.yourdomain.com
cloudflared tunnel run my-tunnel
Or run via Docker Compose:
services:
cloudflared:
image: cloudflare/cloudflared:2024.12.2
container_name: cloudflared
restart: unless-stopped
command: tunnel run
environment:
TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}
Pros and Cons
| Pros | Cons |
|---|---|
| No open ports | Cloudflare sees all traffic (no E2E encryption) |
| Free SSL certificates | Some protocols not supported (raw TCP limited) |
| DDoS protection included | Cloudflare TOS prohibits streaming video through free tunnels |
| No static IP needed | Adds latency (traffic goes through Cloudflare edge) |
| Zero-trust access policies | Vendor lock-in |
Best for: Websites, APIs, web apps you want publicly accessible. Not for Jellyfin/Plex streaming (TOS issue) or gaming servers (UDP performance).
See our full guide: How to Set Up Cloudflare Tunnel
Method 2: VPN Tunnel (Recommended for Personal Access)
A VPN gives you a private encrypted tunnel to your home network. All your services remain unexposed to the internet — you access them as if you were on your local network.
Option A: Tailscale / Headscale (Easiest)
Tailscale is a mesh VPN built on WireGuard. Install the client on your server and your devices — they connect directly, peer-to-peer. Headscale is the self-hosted control server.
# On your server
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# On your phone/laptop — install the Tailscale app
# Both devices now see each other on the Tailscale network
Access your services using the Tailscale IP: http://100.x.x.x:8080
Option B: WireGuard (Most Control)
WireGuard is a kernel-level VPN. Faster than Tailscale (no control plane overhead) but requires manual key management.
services:
wireguard:
image: lscr.io/linuxserver/wireguard:1.0.20210914
container_name: wireguard
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
PUID: 1000
PGID: 1000
TZ: UTC
SERVERURL: your-public-ip-or-ddns
SERVERPORT: 51820
PEERS: phone,laptop,tablet
PEERDNS: auto
ports:
- 51820:51820/udp
volumes:
- ./wireguard-config:/config
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
This generates QR codes for each peer in ./wireguard-config/peer_*/. Scan with the WireGuard mobile app.
VPN Comparison
| Feature | Tailscale | Headscale | WireGuard |
|---|---|---|---|
| Setup time | 2 minutes | 15 minutes | 30 minutes |
| Control plane | Tailscale cloud | Self-hosted | Manual |
| NAT traversal | Automatic (DERP relays) | Automatic | Manual (port forward needed) |
| Key management | Automatic | Automatic | Manual |
| Max devices (free) | 100 | Unlimited | Unlimited |
| Speed | Fast | Fast | Fastest |
| Requires open port | No | No | Yes (UDP 51820) |
Best for: Accessing all your services privately from any device. Tailscale for ease, WireGuard for performance and full control.
See our guides: WireGuard Setup | Headscale Setup | Tailscale vs WireGuard
Method 3: Port Forwarding + Reverse Proxy
The traditional approach: forward ports on your router to your server, and use a reverse proxy with SSL to serve your applications.
Setup
- Get a domain and point it to your public IP (use DDNS if your IP is dynamic)
- Forward ports on your router:
- Port 80 → your server IP:80 (HTTP, for SSL certificate challenges)
- Port 443 → your server IP:443 (HTTPS)
- Set up a reverse proxy (Nginx Proxy Manager, Caddy, or Traefik)
- Get SSL certificates — Let’s Encrypt via your reverse proxy
- Add proxy hosts for each service
Security Hardening (Required)
Port forwarding exposes your home IP address and server directly to the internet. You must harden:
# Enable firewall — only allow SSH, HTTP, HTTPS
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# Install fail2ban
sudo apt install fail2ban
sudo systemctl enable fail2ban
# Keep system updated
sudo apt update && sudo apt upgrade -y
Pros and Cons
| Pros | Cons |
|---|---|
| Full control, no third parties | Exposes your home IP |
| No bandwidth limits | Requires static IP or DDNS |
| Lowest latency | You manage security entirely |
| Works with any protocol | ISP may block ports 80/443 |
Best for: Users who want full control and have a static IP or reliable DDNS. Requires strong security practices.
Method 4: VPS Reverse Proxy
Rent a cheap VPS, run your reverse proxy on it, and tunnel traffic back to your home server via WireGuard. Your home IP stays hidden, you get a static IP, and the VPS handles SSL.
Internet → VPS (public IP) → WireGuard tunnel → Home server
Setup Overview
- Rent a VPS ($5-12/month — Hetzner, DigitalOcean, Linode)
- Install WireGuard on both VPS and home server
- Install a reverse proxy on the VPS (Caddy or Nginx)
- Proxy requests through the WireGuard tunnel to your home server’s WireGuard IP
This combines the best of both worlds: public accessibility without exposing your home IP, and no third-party middleman seeing your traffic.
Best for: Privacy-conscious users who need public services but don’t want to expose their home IP or use Cloudflare.
Decision Guide
Do you need PUBLIC access (anyone on the internet)?
├── Yes → Is it a website or web app?
│ ├── Yes → Cloudflare Tunnel (easiest, free)
│ └── No (gaming, streaming, raw TCP) → Port forwarding or VPS reverse proxy
└── No (just you and your devices) → VPN
├── Want easiest setup? → Tailscale
├── Want self-hosted control? → Headscale
└── Want maximum performance? → WireGuard
Common Mistakes
- Exposing services without SSL — Never run HTTP over the public internet. Use HTTPS via Let’s Encrypt.
- Using port forwarding for everything — If only you need access, use a VPN. Port forwarding exposes attack surface unnecessarily.
- Forgetting DDNS — If your ISP assigns dynamic IPs, your domain stops working when the IP changes. Use DDNS.
- No firewall on port-forwarded servers — If you forward ports, run
ufworiptablesandfail2ban. No exceptions. - Cloudflare Tunnel for streaming — Cloudflare’s free plan TOS prohibits serving large media files. Use a VPN or port forwarding for Jellyfin/Plex.
Next Steps
- Set up Nginx Proxy Manager for SSL and virtual hosts
- Configure Cloudflare Tunnel for zero-port-opening access
- Deploy WireGuard for private remote access
- Learn Docker Networking to understand container connectivity
- Set up Security Basics for SSH hardening and firewall rules
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments