How to easily self-host at home and put your projects online under CGNAT

Self-Hosting at Home: Powerful Server + Tiny VPS + Rock-Solid SSH Reverse Tunnels (No Cloudflare Tunnel, No Ngrok)

5 months ago   •   2 min read

By Aquasp
Table of contents

Introduction

This guide shows you exactly how to do the same — the old-school, bulletproof way using SSH reverse tunnels.

No Cloudflare Tunnel. No Ngrok. Just SSH + systemd.

The Downsides (and Why They’re Manageable)

  • Home internet isn’t datacenter-grade (outages happen)
  • Most ISPs use CGNAT → you can’t open ports normally

Solution: Use a cheap VPS as a public “jump box”. Your heavy server stays home. The VPS only forwards ports.

How It Works – The Magic of Reverse SSH Tunnels (-R)

Internet → Cheap VPS (public IP) → SSH reverse tunnel → Your home server (behind CGNAT)

Your home server initiates an outbound SSH connection to the VPS and says:  
“Anything that hits port 8096 on you → send it to my local Jellyfin on 8096”

Zero ports opened on your home router. Zero exposure.

## Step-by-Step Setup

### 1. On Your Home Server (the powerful one)

```bash
# Create folder for tunnel configs
sudo mkdir -p /etc/sshtunnels

# Example: expose Jellyfin (port 8096)
sudo nano /etc/sshtunnels/jellyfin.conf

Content of the file:

8096:8096   # remote_port:local_port
443:8443    # optional: HTTPS reverse proxy on VPS → your local 8443
80:8080

One line per service. First number = port on the VPS, second = port on your home server.

2. Generate an SSH Key (if you don’t have one)

ssh-keygen -t ed25519 -C "home-server-tunnel"

Copy the public key to your VPS:

ssh-copy-id user@your-vps-ip

3. Create the Tunnel Manager Script

sudo nano /usr/local/bin/sshtunnel.sh
#!/bin/bash

# === EDIT THESE ===
REMOTE_USER="root"                  # or your VPS user
REMOTE_HOST="123.45.67.89"          # your VPS public IP
SSH_KEY="/home/youruser/.ssh/id_ed25519"
SSH_PORT="22"                       # change if you use a non-standard port
# ==================

INSTANCE="$1"
CONFIG_FILE="/etc/sshtunnels/${INSTANCE}.conf"

if [[ ! -f "$CONFIG_FILE" ]]; then
    echo "Error: Config file $CONFIG_FILE not found!"
    exit 1
fi

# Build -R arguments
FORWARD_OPTS=""
while IFS=: read -r remote_port local_port; do
    [[ -z "$remote_port" || "$remote_port" =~ ^# ]] && continue
    # Clean any old process using the remote port
    ssh -p "$SSH_PORT" "$REMOTE_USER@$REMOTE_HOST" \
        "lsof -i :$remote_port -t | xargs -r kill -9" 2>/dev/null
    FORWARD_OPTS="$FORWARD_OPTS -R $remote_port:localhost:$local_port"
done < "$CONFIG_FILE"

echo "Starting tunnel $INSTANCE → $REMOTE_HOST ($FORWARD_OPTS)"

exec ssh -o StrictHostKeyChecking=no \
     -o ServerAliveInterval=30 \
     -o ServerAliveCountThreshold=3 \
     -o ExitOnForwardFailure=yes \
     -o GatewayPorts=yes \
     -N -T \
     -i "$SSH_KEY" \
     -p "$SSH_PORT" \
     $FORWARD_OPTS \
     "$REMOTE_USER@$REMOTE_HOST"

Make it executable:

sudo chmod +x /usr/local/bin/sshtunnel.sh

4. Create a Systemd Service (Auto-Start & Auto-Reconnect)

sudo nano /etc/systemd/system/sshtunnel@.service
[Unit]
Description=SSH Reverse Tunnel for %i
After=network-online.target
Wants=network-online.target

[Service]
User=youruser              # ← change to your home user (not root!)
Group=youruser
ExecStart=/usr/local/bin/sshtunnel.sh %i
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Reload and enable:

sudo systemctl daemon-reload

# Start a tunnel (example: jellyfin)
sudo systemctl enable --now sshtunnel@jellyfin.service

Check status:

sudo systemctl status sshtunnel@jellyfin.service

Install a tiny web server or Caddy/nginx to terminate TLS and proxy to the forwarded ports.

Example with Caddy (automatic HTTPS):

# On the VPS
apt install caddy

# /etc/caddy/Caddyfile
jellyfin.yourdomain.com {
    reverse_proxy localhost:8096
}

Now jellyfin.yourdomain.com → your home Jellyfin, fully encrypted.

All running on my beast home server behind CGNAT.

Pros of This Setup

  • Works behind any CGNAT / ISP block
  • No third-party dependency (no Cloudflare, no Ngrok)
  • Full encryption possible
  • Survives reboots (systemd + Restart=always)
  • Costs almost nothing

Final Words

This is the one really cool way I’ve found to self-host heavy services at home in 2025.

Your powerful hardware stays home. Your $1/month VPS is just a traffic cop.

If you enjoyed this guide, share it with a friend or subscribe to The Self Hosting Art.

Thank you for reading — now go build your unstoppable home lab!

Spread the word

Keep reading