Getting Started
Install Tailscale on a laptop and server, ssh by hostname, expose a service via Cloudflare Tunnel
Getting Started
This page covers the two essentials: Tailscale (mesh VPN for your team) and Cloudflare Tunnel (expose internal services without a public IP). Together they cover most networking needs of a small-to-mid team.
Part 1: Tailscale
Sign Up
- tailscale.com → log in with Google / Microsoft / GitHub / OIDC.
- Your tailnet is created automatically. Tailnet = your private network, one per organization.
Install on Your Laptop
# macOS
brew install --cask tailscale
open -a Tailscale
# Linux
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# Windows: download the installer from tailscale.comAfter tailscale up, log in via the URL printed. Your laptop is now a node in your tailnet with a stable IP and DNS name.
tailscale ip # your tailnet IP, e.g. 100.x.y.z
tailscale status # all devices in the tailnetInstall on a Server
# On the server (Linux)
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh # the --ssh flag enables Tailscale SSHThat --ssh enables Tailscale SSH — your devices in the tailnet can ssh to this server without managing SSH keys:
# From your laptop
ssh user@my-server # works using Tailscale's identity, not ~/.ssh/keysmy-server is the device name; MagicDNS auto-resolves it within the tailnet. No DNS records, no IP memorization.
Access Internal Services
If you have a service on my-server:8080, it's reachable from your laptop:
curl http://my-server:8080
# Or even via the tailnet IP:
curl http://100.x.y.z:8080No port-forwarding. No public IP. The traffic is end-to-end WireGuard-encrypted between your laptop and the server.
Tags and ACLs
By default, every device can talk to every other device. For real teams, restrict access:
// tailnet ACL policy (managed in the Tailscale admin UI)
{
"tagOwners": {
"tag:ci": ["group:engineers"],
"tag:prod": ["group:admins"]
},
"acls": [
// engineers can ssh to all devices
{ "action": "accept", "src": ["group:engineers"], "dst": ["*:22"] },
// CI can reach the staging API on port 8080
{ "action": "accept", "src": ["tag:ci"], "dst": ["staging-api:8080"] },
// prod is admins-only
{ "action": "accept", "src": ["group:admins"], "dst": ["tag:prod:*"] }
]
}Tag a device by running tailscale up --advertise-tags=tag:prod. Group membership comes from your identity provider (Google Workspace groups, Okta, etc.).
This is the zero-trust part — every connection authenticated and authorized.
Exit Nodes
Make a server in your tailnet act as a VPN exit (your laptop's internet traffic routes through it):
# On the exit-node server
sudo tailscale up --advertise-exit-node
# On your laptop, select the exit node
tailscale up --exit-node=<server-name>Useful for accessing geo-restricted services from a specific region, or routing through a corporate-IP-allowlisted gateway.
Subnet Routers
To make a whole subnet reachable (e.g., your AWS VPC at 10.0.0.0/16):
# On a server inside the VPC
sudo tailscale up --advertise-routes=10.0.0.0/16
# Approve the route in the Tailscale admin UINow every device in your tailnet can reach 10.x.y.z addresses through that subnet router. Site-to-site VPN without configuring a single firewall rule.
Part 2: Cloudflare Tunnel
For exposing an internal service to the public internet (or to a SaaS via webhook) without a public IP.
Install cloudflared
# macOS
brew install cloudflared
# Linux
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
# Login
cloudflared tunnel login # opens browser to authorize your domainCreate a Tunnel
cloudflared tunnel create my-tunnel
# Creates a tunnel with a UUID and stores credentials in ~/.cloudflared/
# Route a hostname to the tunnel
cloudflared tunnel route dns my-tunnel internal.example.comConfigure What to Expose
# ~/.cloudflared/config.yml
tunnel: my-tunnel
credentials-file: /home/user/.cloudflared/<UUID>.json
ingress:
- hostname: internal.example.com
service: http://localhost:8080
- hostname: grafana.example.com
service: http://localhost:3000
- hostname: ssh.example.com
service: ssh://localhost:22
- service: http_status:404 # default fallbackRun the Tunnel
cloudflared tunnel run my-tunnel
# Or install as a system service
sudo cloudflared service installNow https://internal.example.com is publicly reachable — but only through Cloudflare. The internal service has no public IP; cloudflared makes an outbound connection to Cloudflare.
Add Cloudflare Access (Authentication)
Without Access, internal.example.com is open to anyone with the URL. Add Cloudflare Access to require SSO:
- Cloudflare dashboard → Zero Trust → Access → Applications.
- Add an application → Self-hosted.
- Application domain:
internal.example.com. - Policies: who can access?
Action: Allow
Include: Emails ending in @example.com
Require: Country is one of (US, CA, UK)
Require: Authentication method is SSO via Google WorkspaceNow visiting https://internal.example.com redirects to Google login. Only users matching the policy reach the service.
Cloudflare Access also issues a JWT to the request the service sees — verify it in your app to enforce identity:
Cf-Access-Authenticated-User-Email: alice@example.com
Cf-Access-Jwt-Assertion: eyJ...A Common Recipe
The combo most teams end up with:
Engineers ──► Tailscale ──► production SSH, internal admin panels, K8s API
(peer-to-peer)
Customers ──► Cloudflare Tunnel + Access ──► self-hosted apps requiring SSO
(public-facing with auth)
Service-to- ──► Service Mesh ──► mTLS, traffic management
service (within cluster)Each tool does its layer; together they replace the entire "perimeter firewall" model.
Tear Down
# Tailscale
tailscale logout
sudo apt remove tailscale # or uninstall via the GUI
# Cloudflare Tunnel
cloudflared tunnel delete my-tunnel
sudo cloudflared service uninstallWhat's Next
You can mesh your devices and expose services without a public IP. Next:
- Zero Trust Patterns — ACLs, identity-aware access, MagicDNS, exit nodes, subnet routers
- Best Practices — production deployment, key management, observability, scaling