
Finally a good alternative to ngrok.
No public IP address? No problem. Cloudflare Tunnel has your back.
In this tutorial we will learn how to easily install and setup Cloudflare Tunnel / Cloudflared CLI on your Windows, Linux or macOS. You’ll also learn how to setup custom domains to route traffic to your local server without exposing your IP address.
What is Cloudflare Tunnel?
Cloudflare Tunnel is service which allows your local network to be exposed on the internet. Previously we had services like ngrok and localhost.run, now we have Cloudflare Tunnel as an alternative to ngrok.
What do you need to use Cloudflare Tunnel?
Not even a Cloudflare account.
All you need is to install Cloudflared CLI, the CLI which connects our local server to Cloudflare server.
Install Cloudflared CLI
Installing Cloudflared or Cloudflare tunnel CLI is fast as light.
Install Cloudflared CLI on Linux
Arch Linux
sudo pacman -Syu cloudflared
Ubuntu
Show instructions
- Add Cloudflare GPG key:
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
- Add this repo to your apt repositories:
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
- Install cloudflared:
sudo apt-get update && sudo apt-get install cloudflared
Other Linux installation
- Debian Buster
- Debian Bullseye
- Debian Bookworm
- Ubuntu Focal
- Ubuntu 22.04(Jammy Jellyfish)
- Amazon Linux
- RHEL Generic
- Centos 7
- Centos 8
- Centos Stream
Install Cloudflared CLI on MacOS
With Homebrew it’s just one command to rule:
brew install cloudflare/cloudflare/cloudflared
Install Cloudflared CLI on Windows
With winget it’s never been this easy to install packages:
winget install --id Cloudflare.cloudflared
Expose local server with Cloudflare Tunnel CLI
Once you have the cloudflared CLI installed, all you need is this command to expose your local server:
cloudflared tunnel --url localhost:8080
Replace localhost:8080
with your server address and port.
Test hello world server
If you don’t have a server running and you just want to see if Cloudflare tunnel works in your system, you can use the --hello-world
argument to run the demo server.
cloudflared tunnel --hello-world
Command output
You’ll get your tunnel link just like ngrok and with other output like this from the cloudflared
command:
INF Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee. If you intend to use Tunnels in production you should use a pre-created named tunnel by following: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
INF Requesting new quick Tunnel on trycloudflare.com...
INF +--------------------------------------------------------------------------------------------+
INF | Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): |
INF | https://corporate-mention-tiles-coordinates.trycloudflare.com |
INF +--------------------------------------------------------------------------------------------+
If you open the link this is what you’ll see a page with Congrats! You created a tunnel!:

Login to Cloudflare to Get Custom Domains
Wouldn’t it be great if we could have tunnel.learnaw.io
which will expose my local dev server?
We couldn’t get free custom or static domains before.
After the launch of Cloudflare Tunnel ngrok announced free static domain.
Setup website on Cloudflare
If you don’t a website setup on Cloudflare you’ll need to do that before we proceed.
- Create your free account on Cloudflare
- Create and setup your website DNS
- Update your nameservers to point to Cloudflare
Login to Cloudflare Tunnel CLI
Once we have setup our website on cloudlfare we can now login to cloudlared CLI with:
cloudflared tunnel login
- You’ll be redirected to Cloudflare dashboard, if not open the link manually.
- You’ll have to select your site by clicking on it.
- Authorize it by clicking on Authorize button.

You’ll see You have successfully logged in:
❯ cloudflared tunnel login
A browser window should have opened at the following URL:
https://dash.cloudflare.com/argotunnel?aud=&callback=https%3A%2F%2Flogin.cloudflareaccess.org%2FEKMdVrNi54DCmqPY24eNOn0hL1ag4seTqQo8hgo95WY%3D
If the browser failed to open, please visit the URL above directly in your browser.
2023-09-14T08:24:07Z INF Waiting for login...
You have successfully logged in.
If you wish to copy your credentials to a server, they have been saved to:
/home/shivam/.cloudflared/cert.pem
Create a tunnel and configure it
Create tunnel
Using the cloudflared tunnel create
command we create a tunnel for us which will generate config file in ~/.cloudflared
directory.
cloudflared tunnel create learnaws
This gives us the UUID of the tunnel as well:
❯ cloudflared tunnel create learnaws
Tunnel credentials written to /home/shivam/.cloudflared/7d1edf62-1efe-4a5c-a2ea-b66a5b6d34a8.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.
Created tunnel learnaws with id 7d1edf62-1efe-4a5c-a2ea-b66a5b6d34a8
Verify the tunnel you created by listing tunnels
With cloudflared tunnel list
command you can see all the tunnels you’ve created.
❯ cloudflared tunnel list
You can obtain more detailed information for each tunnel with `cloudflared tunnel info <name/uuid>`
ID NAME CREATED CONNECTIONS
7d1edf62-1efe-4a5c-a2ea-b66a5b6d34a8 learnaws 2023-09-14T08:38:26Z 1xbom06, 1xbom12, 2xdel03
Add Subdomain for your tunnel
Now we need to add DNS record which will route traffic to our tunnel.
With cloudflared CLI we can easily do that with tunnel route dns
command:
cloudflared tunnel route dns learnaws tunnel
learnaws
is the name of the tunnel which we created earliertunnel
is the subdomain name which routes traffic fromtunnel.learnaws.io
On successful DNS creation you’ll receive the message saying: Added CNAME tunnel.learnaws.io which will route to this tunnel tunnelID=7d1edf62-1efe-4a5c-a2ea-b66a5b6d34a8
Create tunnel config file
Default cloudflared directory location for config file:
OS | Path to default directory |
---|---|
Windows | %USERPROFILE%\.cloudflared |
macOS and Unix-like systems | ~/.cloudflared , /etc/cloudflared , and /usr/local/etc/cloudflared , in this order. |
Inside the cloudflared config directory we create a config file config.yaml
and specify where to route the traffic.
cd ~/.cloudflared/
vim config.yaml
- Edit your config in the format below
- Save your file
:wq
tunnel: your-tunnel-uuid
credentials-file: /home/shivam/.cloudflared/your-tunnel-uuid.json
ingress:
# define hostname matching
- hostname: tunnel.learnaws.io
# proxy request to localhost:3000
service: http://localhost:3000
# if nothing is matched return 404
- service: http_status:404
Read more on config files and syntax on the Cloudflare docs.
Ingress Supported services
Cloudflare tunnel supports many protocols including HTTP/S, you can use the list below to see what services you can connect with it. Learn more about Ingress configuration on the official docs.
Service | Description | Example service value |
---|---|---|
HTTP/S | Incoming HTTP requests are proxied directly to your local service. | https://localhost:8000 |
HTTP over Unix socket | Just like HTTP, but using a Unix socket instead. | unix:/home/production/echo.sock |
HTTPS over Unix socket | Just like HTTPS, but using a Unix socket instead. | unix+tls:/home/production/echo.sock |
TCP | TCP connections are proxied to your local service. | tcp://localhost:2222 |
SSH | SSH connections are proxied to your local service. https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/use-cases/ssh/. | ssh://localhost:22 |
RDP | RDP connections are proxied to your local service. https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/use-cases/rdp/. | rdp://localhost:3389 |
kubectl bastion mode | cloudflared will act like a jumphost, allowing access to any local address. | bastion |
Hello World | Test server for validating your Cloudflare Tunnel setup. | hello_world |
HTTP status | Responds to all requests with the given HTTP status. | http_status:404 |
Start Cloudflare Tunnel
To start serving request use cloudflared tunnel run
and it’ll start proxy server on your custom domain.
❯ cloudflared tunnel run
Starting tunnel tunnelID=7d1edf62-1efe-4a5c-a2ea-b66a5b6d34a8
Version 2023.8.2
GOOS: linux, GOVersion: go1.20.7, GoArch: amd64
Settings: map[cred-file:/home/shivam/.cloudflared/7d1edf62-1efe-4a5c-a2ea-b66a5b6d34a8.json credentials-file:/home/shivam/.cloudflared/7d1edf62-1efe-4a5c-a2ea-b66a5b6d34a8.json]
cloudflared will not automatically update if installed by a package manager.
Generated Connector ID: a50c77ff-bbb0-41ff-9ae2-f112a54c2245
Initial protocol quic
ICMP proxy will use 192.168.29.172 as source for IPv4
ICMP proxy will use 2405:201:4028:e83f:5fb6:484f:25b:5322 in zone enp58s0 as source for IPv6
Starting metrics server on 127.0.0.1:46179/metrics
Registered tunnel connection connIndex=0 connection=cc36911b-
Now if you go to your subdomain eg. tunnel.learnaws.io
you’ll see the output from server:

Here I am simply running a server which returns JSON.
Run Cloudflare Tunnel all the time as a service
If we close our terminal our tunnel will be stopped as well, if we want to keep the tunnel running we can use cloudflared
service.
Install cloudflared
service
sudo cloudflared --config ~/.cloudflared/config.yml service install
On successful install you’ll see the message:
2023-09-14T09:23:07Z INF Using Systemd
2023-09-14T09:23:09Z INF Linux service for cloudflared installed successfully
Update config file
The service might take config file from /etc/cloudflared/config.yml
, to change the location you can edit the service config file with your default location eg. ~/.cloudflared/config.yml
).
sudo vim /etc/systemd/system/cloudflared.service;
Make sure to reload daemon and restart your service after updating the service file:
sudo systemctl daemon-reload
sudo systemctl restart cloudflared
Start cloudflared
service
systemctl start cloudflared
Check the service status
systemctl status cloudflared
You should see it as active and running:
cloudflared.service - cloudflared
Loaded: loaded (/etc/systemd/system/cloudflared.service; enabled; preset: disabled)
Active: active (running) since Thu 2023-09-14 14:53:09 IST; 4min 15s ago
Main PID: 48301 (cloudflared)
Tasks: 16 (limit: 18921)
Memory: 21.1M
CPU: 201ms
CGroup: /system.slice/cloudflared.service
└─48301 /usr/bin/cloudflared --no-autoupdate --config /etc/cloudflared/config.yml tunnel run
Now even if you restart your system or close all your terminals your tunnel will keep running in the background.
MacOS and Windows service
You can find documentation on how to configure the services on your OS:
Cloudflared CLI commands to Keep Handy
You might be experimenting with Cloudflare Tunnels by creating them, setting up multiple routes and tunnels, listing the tunnels and what not.
Head over to the official page for useful tunnel commands to see the complete list for commands.
Spit out your coffee NOW!
When are you sharing this amazing tool with your dev circle? It’s now or never. They’ll thank you for making their life so much easier.
Tried out my guide and have some insights to share? Comment below and I’ll reply fast.
No more ngrok!