Ahmet ALMAZDoom Face

Ahmet ALMAZ

Full Stack Developer from Türkiye 🇹🇷

VI VI VI Editor of the BeastVim vs Emacs

Secure SSH Access Using Cloudflare Tunnel With No Open Ports

updated:
created:
words:611 words
time:4 min
0

I set up developers.cloudflare.comCloudflare Tunnel github.com(cloudflared), to stop worrying about open SSH ports on my server. No public IP exposed, no ports forwarded, no DDNS to maintain.

Instead of opening a port and hoping for the best, I let Cloudflare handle the connection entirely. My server initiates an outbound tunnel to Cloudflare’s edge, and all SSH traffic reaches it through that tunnel, not through any open port on my firewall. No port scanners can find me, no brute-force bots can knock, no DDoS can reach my actual server.

Every connection is authenticated at Cloudflare’s gateway before it ever touches my infrastructure. Paired with www.cloudflare.comCloudflare Zero Trust policies, this is about as close to “set it and forget it” secure remote access as I’ve found.

#Prerequisites

  • A domain managed by Cloudflare
  • Docker installed on your server
  • A Cloudflare Zero Trust account

#1. Create Tunnel in Cloudflare Dashboard

  1. Open one.dash.cloudflare.comCloudflare Zero Trust Dashboard
  2. Navigate to Networks → Connectors
  3. Click Create a tunnel
  4. Select Cloudflared as the connector type
  5. Name your tunnel (e.g., server)
  6. Select Docker as your environment
  7. Copy the token displayed in the installation command

#2. Docker Setup on Server

Create a docker-compose.yml file with the token embedded directly:

name: cloudlfared
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel run
environment:
TUNNEL_TOKEN: <your-token-here>
extra_hosts:
- "host.docker.internal:172.18.0.1"
networks:
- cftunnel
networks:
cftunnel:
external: true

Replace <your-token-here> with the token from the previous step.

This configuration uses host.docker.internal instead of network_mode: host. The key advantage: it keeps cloudflared on its own network while still reaching host services. With network_mode: host, the container would share the host’s entire network stack, effectively exposing every container port on the machine.

By using a dedicated network with extra_hosts, you can run services like Navidrome, Jellyfin, or a web app without exposing any ports to the local network, only cloudflared has a route to them, and it only forwards traffic for the specific public hostnames you configure in the Cloudflare dashboard.

Create the external network beforehand:

Terminal window
docker network create cftunnel

Then start the container:

Terminal window
docker compose up -d

Back in the Cloudflare dashboard, your connector should now show as connected.


#3. Add SSH Public Hostname

  1. Go to Networks → Connectors in Cloudflare Dashboard
  2. Click your tunnel name
  3. Click the Published application routes tab
  4. Click Add a published application route
  5. Configure:
FieldValue
Subdomainssh
DomainSelect your domain
TypeSSH
URLlocalhost:22

Your SSH endpoint is now: ssh.yourdomain.com


#4. Create Access Application (Authentication Layer)

  1. Go to Access controls → Applications
  2. Click Create new application
  3. Select Self-hosted and private, then Private destinations, then Continue with Self-hosted and private
  4. Configure:
FieldValue
Public hostnamesssh.yourdomain.com
NameSSH Access
Session Duration24 hours
  1. Under Access policies, click Create new policy:
FieldValue
Policy nameAllow SSH
ActionAllow
SelectorEmails
Valueyour.email@example.com
  1. Under Authentication, ensure MFA is disabled.

  2. Click Create.


#5. Client Setup

#Install cloudflared

Follow the developers.cloudflare.comofficial installation guide for your OS.

#Configure SSH

Add the following to ~/.ssh/config:

Host servername
HostName ssh.yourdomain.com
User server-username
ProxyCommand cloudflared access ssh --hostname %h
IdentityFile ~/.ssh/id_ed25519

#Connect

Terminal window
ssh servername

A browser window will open for authentication. Enter the email you configured earlier, verify the code sent to your inbox, and you’re connected.


#Conclusion

That’s it. SSH through Cloudflare Tunnel, zero open ports, with an authentication layer that keeps everything locked down. I’ve been running this setup for months now and it’s been rock solid, no failed connections, no security scares, no maintenance headaches.

The best part? Once the tunnel is up, you can repeat Step 3 to expose any other service running on your server, Navidrome, Jellyfin, Home Assistant, you name it, just by adding more public hostnames. No firewall rules, no reverse proxy config, no port forwarding. It just works.