Devops

Cloudflare Tunnel Management for Multiple Projects

Published 2026-07-21 ~471 words Tags: cloudflare, tunnels, hosting, multiple-projects

Once you have more than one project running on the same machine, Cloudflare Tunnels become essential. Instead of juggling ports and firewall rules for each project, you route everything through Cloudflare with automatic HTTPS.

The Multi-Project Architecture

Each project runs on its own local port. A separate tunnel (or a single tunnel with multiple hostnames) maps each domain to its port:

| Domain | Local Port | Service | |---|---|---| | handycanadian.ca | 8897 | PHP Router | | unofficialtutorials.com | 8898 | PHP Router | | flashmart.ca | 8899 | PHP Router |

Single Tunnel With Multiple Hostnames

Instead of running a separate cloudflared process per project, you can use a single tunnel with an ingress rules file:

# ~/.cloudflared/config.yaml
tunnel: my-main-tunnel
credentials-file: /root/.cloudflared/my-main-tunnel.json
ingress:
  - hostname: handycanadian.ca
    service: http://localhost:8897
  - hostname: unofficialtutorials.com
    service: http://localhost:8898
  - hostname: flashmart.ca
    service: http://localhost:8899
  - service: http_status:404

Route each DNS record:

cloudflared tunnel route dns my-main-tunnel handycanadian.ca
cloudflared tunnel route dns my-main-tunnel unofficieltutorials.com
cloudflared tunnel route dns my-main-tunnel flashmart.ca

Then run a single tunnel process:

cloudflared tunnel run my-main-tunnel

Per-Project Tunnels (Isolation)

If you prefer isolation (one project crashing shouldn't affect others), run separate tunnels:

# Terminal 1
cloudflared tunnel run handycanadian
# Terminal 2
cloudflared tunnel run unofficialtutorials
# Terminal 3
cloudflared tunnel run flashmart

Each project can be started, stopped, or updated independently.

Managing With systemd

Create a service file per project or one for the multi-tunnel:

# /etc/systemd/system/cloudflared-tunnels.service
[Unit]
Description=Cloudflare Tunnels
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/cloudflared tunnel run
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target

Then:

systemctl enable cloudflared-tunnels
systemctl start cloudflared-tunnels
systemctl status cloudflared-tunnels

Starting Projects On Boot

For PHP development servers, create a matching service per project:

# /etc/systemd/system/handycanadian.service
[Unit]
Description=HandyCanadian PHP Dev Server
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/php -S 0.0.0.0:8897 -t /home/jarvis/_projects/handycanadian.ca
WorkingDirectory=/home/jarvis/_projects/handycanadian.ca
Restart=always
User=jarvis
[Install]
WantedBy=multi-user.target

Health Checks

Set up a simple cron job to verify all tunnels are up:

#!/bin/bash
for domain in handycanadian.ca unofficialtutorials.com flashmart.ca; do
    if ! curl -sfI "https://$domain" > /dev/null 2>&1; then
        echo "$domain is down at $(date)" >> /var/log/tunnel-health.log
        systemctl restart cloudflared
    fi
done

Pro Tips

Port conflicts. Pick a port range well above 1024 (non-privileged) and document which port belongs to which project. Nothing worse than starting a new project only to find port 8897 is already taken.

Logs. Each tunnel logs to journald. View per-tunnel logs with:

journalctl -u cloudflared-tunnels -f

Updates. Cloudflared updates frequently. Set up a weekly cron to update it:

cloudflared update

With this setup, you can spin up a new project in minutes: assign a port, add an ingress rule, route DNS, and you're live.