Skip to content

Instantly share code, notes, and snippets.

@decagondev
Last active November 13, 2025 14:07
Show Gist options
  • Select an option

  • Save decagondev/99c4ed55626e33c74e5695787be0e1e7 to your computer and use it in GitHub Desktop.

Select an option

Save decagondev/99c4ed55626e33c74e5695787be0e1e7 to your computer and use it in GitHub Desktop.

Cloudflare Tunnel → EC2 (Amazon Linux) — Step‑by‑Step

Goal: expose two hostnames via Cloudflare Tunnel that route to services running on a single EC2 instance:

  • <SUBDOMAIN 1>.<YOUR DOMAIN NAME>.comhttp://localhost:80 (Python website)
  • <SUBDOMAIN 2>.<YOUR DOMAIN NAME>.comhttp://localhost:3000 (Express API)

This guide is concise and written for Amazon Linux (YUM/RPM). Commands assume you run them as ec2-user with sudo where necessary.


Quick overview (what we'll do)

  1. Install cloudflared on the EC2 instance.
  2. Authenticate cloudflared to your Cloudflare account.
  3. Create a named tunnel and save its credentials.
  4. Create a config.yml with ingress rules for :80 and :3000.
  5. Point DNS (CNAME) records for your subdomains to the tunnel.
  6. Install cloudflared as a system service and start it.
  7. Test and debug.

Prerequisites

  • EC2 instance running Amazon Linux (user: ec2-user).
  • The website is listening on localhost:80; Express API on localhost:3000.
  • You own the Cloudflare zone for <YOUR DOMAIN NAME> and can add DNS records and create tunnels in the Cloudflare Zero Trust / Tunnels section.
  • curl, sudo, and systemd are available (default on Amazon Linux).

1) Install cloudflared

On Amazon Linux this guide defaults to the binary install which works reliably.

# download binary and install
cd /home/ec2-user
curl -LO "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64"
sudo mv cloudflared-linux-amd64 /usr/local/bin/cloudflared
sudo chmod +x /usr/local/bin/cloudflared

# verify
cloudflared --version

If you prefer, you can also install via a package manager on other distros — but for Amazon Linux the binary above is the recommended approach.


2) Authenticate cloudflared with your Cloudflare account

Run the login command which opens an authentication URL (you may need to run from a machine with a browser). On a headless EC2, it prints a URL to open on your laptop.

cloudflared tunnel login

This creates a cert.pem in ~/.cloudflared/ and allows cloudflared to create tunnels in your Cloudflare account.


3) Create a named tunnel

Choose a name (e.g. my-cf-tunnel). Creating a tunnel produces a credentials file (JSON) stored under ~/.cloudflared/ and prints its UUID.

cloudflared tunnel create my-cf-tunnel
# Example output -> Tunnel credentials written to /home/ec2-user/.cloudflared/<UUID>.json
# Note the UUID printed by the command (you'll use it in DNS or config)

Make a note of the UUID. You can list tunnels anytime:

cloudflared tunnel list

4) Create the config.yml for routing (ingress)

Create the Cloudflared config directory and the config file:

mkdir -p /home/ec2-user/.cloudflared
sudo chown ec2-user:ec2-user /home/ec2-user/.cloudflared

Create /home/ec2-user/.cloudflared/config.yml with the following contents (replace <UUID> with your tunnel UUID):

# /home/ec2-user/.cloudflared/config.yml

tunnel: <UUID>
credentials-file: /home/ec2-user/.cloudflared/<UUID>.json

originRequest:
  # if your local site uses self-signed certs or you intentionally want Cloudflare not to verify
  noTLSVerify: true

ingress:
  - hostname: <SUBDOMAIN 1>.<YOUR DOMAIN NAME>.com
    service: http://localhost:80
  - hostname: <SUBDOMAIN 2>.<YOUR DOMAIN NAME>.com
    service: http://localhost:3000
  - service: http_status:404

Notes:

  • noTLSVerify: true is optional. If your origin serves proper TLS certs, remove or set to false.
  • Keep file permissions safe: chmod 600 /home/ec2-user/.cloudflared/<UUID>.json.

5) Create DNS CNAME records that point your subdomains to the tunnel

There are two ways:

A. Let cloudflared create DNS records for you (recommended if you used cloudflared tunnel login and have the cert installed):

# create CNAME records in Cloudflare DNS for each hostname
cloudflared tunnel route dns <UUID or NAME> <SUBDOMAIN 1>.<YOUR DOMAIN NAME>.com
cloudflared tunnel route dns <UUID or NAME> <SUBDOMAIN 2>.<YOUR DOMAIN NAME>.com

B. Manually in the Cloudflare Dashboard

  • Go to the DNS settings for <YOUR DOMAIN>.

  • Add two CNAME records:

    • Name: <SUBDOMAIN 1> → Target: <UUID>.cfargotunnel.com
    • Name: <SUBDOMAIN 2> → Target: <UUID>.cfargotunnel.com
  • Save.

Important: the DNS target must be <UUID>.cfargotunnel.com (the tunnel subdomain). If you use cloudflared tunnel route dns the command will create the records for you.


6) Install cloudflared as a systemd service and start it

To avoid permission/config path issues when running cloudflared as a system service, copy the tunnel credentials and config into a system-level location and point the service at that location.

# create system config dir and copy files
sudo ls /usr/local/etc || true
sudo mkdir -p /usr/local/etc/cloudflared
sudo cp /home/ec2-user/.cloudflared/*.json /usr/local/etc/cloudflared/
sudo cp /home/ec2-user/.cloudflared/*.yml /usr/local/etc/cloudflared/
sudo chown -R root:root /usr/local/etc/cloudflared
sudo chmod 640 /usr/local/etc/cloudflared/*.json

# Create a systemd unit that references the system config
sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl start cloudflared

# check status
sudo systemctl status cloudflared
journalctl -u cloudflared -f

If you still prefer to run the service as ec2-user, adjust the User= line and ensure /usr/local/etc/cloudflared has appropriate ownership and read permissions for that user.


7) Test the setup

  • Ensure cloudflared is running:
sudo systemctl status cloudflared
journalctl -u cloudflared -f
  • Test the tunnel routing from your laptop/browser: visit https://<SUBDOMAIN 1>.<YOUR DOMAIN NAME>.com and https://<SUBDOMAIN 2>.<YOUR DOMAIN NAME>.com.
  • Locally on the EC2 instance, you can check which ingress rule matches:
cloudflared tunnel ingress rule https://<SUBDOMAIN 1>.<YOUR DOMAIN NAME>.com
cloudflared tunnel ingress rule https://<SUBDOMAIN 2>.<YOUR DOMAIN NAME>.com
  • When DNS is newly created, allow some minutes for propagation.

8) Debugging tips

  • Check cloudflared logs: journalctl -u cloudflared -n 200 --no-pager.
  • Confirm the tunnel is connected with: cloudflared tunnel list.
  • Confirm Cloudflare DNS CNAMEs point to <UUID>.cfargotunnel.com.
  • If you see HTTP 1016 or 502 errors, the tunnel may be down or the ingress rule doesn't match; check config.yml syntax and hostnames.
  • If you use noTLSVerify: true it avoids origin TLS validation problems — only use it when appropriate.

9) Security / production notes

  • Cloudflare still fronts TLS termination; you get HTTPS between client and Cloudflare. You can still enable Access policies or firewall rules in Cloudflare Zero Trust for extra protection.
  • Don't expose administrative or internal-only endpoints via public hostnames unless protected by Access policies.

10) Example config.yml (final)

Use the system-level path so the systemd service can find the files. Replace <UUID> with your tunnel UUID.

# /usr/local/etc/cloudflared/config.yml

tunnel: <UUID>
credentials-file: /usr/local/etc/cloudflared/<UUID>.json

originRequest:
  noTLSVerify: true

ingress:
  - hostname: <SUBDOMAIN 1>.<YOUR DOMAIN NAME>.com
    service: http://localhost:80
  - hostname: <SUBDOMAIN 2>.<YOUR DOMAIN NAME>.com
    service: http://localhost:3000
  - service: http_status:404

After you place the config.yml and credential JSON in /usr/local/etc/cloudflared/, make sure the service is reloaded and started (sudo systemctl daemon-reload && sudo systemctl restart cloudflared).


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment