CloudCode is designed to be accessed over a private Tailscale network from your phone or other devices. This guide explains how to set it up securely.
The security model has two layers for Tailscale:
- Network layer — CloudCode only listens on your Tailscale interface (or localhost), never publicly
- Identity layer — CloudCode validates the
X-Tailscale-Userheader to ensure only approved tailnet identities can access the app
Follow the Tailscale installation guide for your platform.
# Linux
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale uptailscale ip -4
# Example output: 100.64.0.1In your .env:
HOST=100.64.0.1 # Your Tailscale IP
PORT=3000
APP_BASE_URL=http://100.64.0.1:3000This ensures CloudCode is only reachable over Tailscale, not the public internet.
CloudCode can validate the X-Tailscale-User header injected by tailscale serve to restrict access to specific tailnet identities.
In your .env:
TAILSCALE_ALLOWED_IDENTITIES=your@tailscale-identity.comTo find your Tailscale identity:
tailscale status --json | jq '.Self.UserID'Or check Tailscale admin console.
tailscale serve provides automatic HTTPS with a *.ts.net domain:
tailscale serve --bg http://localhost:3000This makes CloudCode available at:
https://your-machine-name.tail-abc123.ts.net
With identity headers automatically injected for validation.
Update .env:
APP_BASE_URL=https://your-machine-name.tail-abc123.ts.net
HOST=127.0.0.1 # Listen on localhost since tailscale serve handles the proxy- Install Tailscale on your iPhone/Android
- Connect to your tailnet
- Open
http://<tailscale-ip>:3000or your*.ts.netURL - Log in with your CloudCode credentials
- CloudCode
HOSTis set to Tailscale IP (not0.0.0.0) -
TAILSCALE_ALLOWED_IDENTITIESis configured - Strong
SESSION_SECRETis set (64+ char random string) - Firewall blocks port 3000 from non-Tailscale interfaces
- Consider using
tailscale servefor automatic HTTPS
If using ufw, ensure Tailscale traffic is allowed but the port is blocked on other interfaces:
# Allow Tailscale interface
sudo ufw allow in on tailscale0 to any port 3000
# Block from other interfaces
sudo ufw deny 3000- Verify both devices are on the same tailnet:
tailscale status - Check CloudCode is listening on the right IP:
ss -tlnp | grep 3000 - Ensure firewall allows Tailscale traffic
- Check the
X-Tailscale-Userheader is being sent (requirestailscale serve) - Verify
TAILSCALE_ALLOWED_IDENTITIESmatches exactly (case-sensitive) - Check CloudCode logs:
journalctl -u cloudcode@$USER -f