Steven's Knowledge

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

  1. tailscale.com → log in with Google / Microsoft / GitHub / OIDC.
  2. 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.com

After 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 tailnet

Install on a Server

# On the server (Linux)
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh                 # the --ssh flag enables Tailscale SSH

That --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/keys

my-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:8080

No 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 UI

Now 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 domain

Create 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.com

Configure 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 fallback

Run the Tunnel

cloudflared tunnel run my-tunnel

# Or install as a system service
sudo cloudflared service install

Now 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:

  1. Cloudflare dashboard → Zero Trust → Access → Applications.
  2. Add an application → Self-hosted.
  3. Application domain: internal.example.com.
  4. 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 Workspace

Now 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 uninstall

What'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

On this page