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.