All Articles

How to Set Up Tailscale for Zero-Trust Access to Your DevOps Infrastructure

Step-by-step guide to setting up Tailscale for secure access to Kubernetes clusters, databases, and internal tools without traditional VPNs.

DevOpsBoysMar 18, 20265 min read
Share:Tweet

Traditional VPNs are dying. They're slow, painful to configure, and give users access to entire network segments when they only need one service. Tailscale fixes all of this.

Tailscale is a WireGuard-based mesh VPN that creates encrypted point-to-point connections between your devices. No central gateway, no hairpinning traffic through a data center, no managing OpenVPN configs.

This guide walks you through setting up Tailscale for secure access to your DevOps infrastructure — Kubernetes API servers, databases, internal dashboards, and CI/CD systems.

Why Tailscale Over Traditional VPNs

FeatureTraditional VPNTailscale
Setup timeHours/days5 minutes
Connection modelHub and spokeMesh (P2P)
LatencyHigh (hairpin through gateway)Low (direct connection)
Access controlNetwork-level (full segment)Per-service ACLs
NAT traversalManual port forwardingAutomatic
ProtocolOpenVPN/IPSecWireGuard
Key managementManual certificatesAutomatic rotation

Step 1 — Install Tailscale on Your Machine

macOS

bash
brew install tailscale

Linux

bash
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Windows

Download from tailscale.com and install the app. That's it.

After installing, authenticate with your identity provider (Google, Microsoft, GitHub, etc.). Tailscale uses your existing SSO — no separate user database.

Verify it's working:

bash
tailscale status

You'll see your machine listed with a Tailscale IP (usually 100.x.x.x).

Step 2 — Connect Your Servers

Install Tailscale on every server you want to access:

bash
# On your Kubernetes nodes, CI server, database server, etc.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh

The --ssh flag enables Tailscale SSH, which lets you SSH into the server using your Tailscale identity instead of SSH keys:

bash
# No SSH keys needed — authenticates via Tailscale identity
ssh user@my-server  # uses Tailscale hostname

Step 3 — Access Your Kubernetes Cluster

This is where Tailscale shines for DevOps. Instead of exposing your K8s API server to the internet, put it behind Tailscale.

Option A: Tailscale on the Control Plane Node

Install Tailscale on your control plane node, then update your kubeconfig to use the Tailscale IP:

bash
# On the control plane node
sudo tailscale up --advertise-tags=tag:k8s-server
 
# Get the Tailscale IP
tailscale ip -4
# Output: 100.64.1.15

Update your kubeconfig:

yaml
apiVersion: v1
kind: Config
clusters:
- cluster:
    server: https://100.64.1.15:6443
    certificate-authority-data: <your-ca-data>
  name: production

Now kubectl commands only work from machines on your Tailscale network.

Option B: Tailscale Operator for Kubernetes

For managed Kubernetes (EKS, GKE, AKS), use the Tailscale Kubernetes operator:

bash
helm repo add tailscale https://pkgs.tailscale.com/helmcharts
helm repo update
 
helm install tailscale-operator tailscale/tailscale-operator \
  --namespace tailscale \
  --create-namespace \
  --set oauth.clientId=YOUR_OAUTH_CLIENT_ID \
  --set oauth.clientSecret=YOUR_OAUTH_CLIENT_SECRET

This creates a Tailscale-connected pod in your cluster. You can then expose services through Tailscale:

yaml
apiVersion: v1
kind: Service
metadata:
  name: grafana
  annotations:
    tailscale.com/expose: "true"
    tailscale.com/hostname: "grafana-prod"
spec:
  selector:
    app: grafana
  ports:
  - port: 3000

Now grafana-prod is accessible on your Tailscale network at http://grafana-prod:3000. No ingress controller, no load balancer, no public IP.

Step 4 — Secure Database Access

Stop exposing database ports to the internet. Use Tailscale subnet routers instead.

Set Up a Subnet Router

On a server in the same network as your database:

bash
sudo tailscale up \
  --advertise-routes=10.0.1.0/24 \
  --accept-dns=false

This advertises the 10.0.1.0/24 subnet (where your database lives) to your Tailscale network.

Approve the route in the Tailscale admin console, then from your laptop:

bash
# Connect to your RDS instance through Tailscale
psql -h 10.0.1.50 -U postgres -d production

Your database never needs a public endpoint. Traffic is encrypted end-to-end through WireGuard.

Step 5 — Set Up Access Control Lists (ACLs)

Tailscale ACLs define who can access what. Edit them in the admin console or as code:

json
{
  "acls": [
    {
      "action": "accept",
      "src": ["group:devops"],
      "dst": ["tag:k8s-server:*"]
    },
    {
      "action": "accept",
      "src": ["group:developers"],
      "dst": ["tag:k8s-server:443", "tag:grafana:3000"]
    },
    {
      "action": "accept",
      "src": ["group:dba"],
      "dst": ["tag:database:5432", "tag:database:3306"]
    }
  ],
  "groups": {
    "group:devops": ["user1@company.com", "user2@company.com"],
    "group:developers": ["dev1@company.com", "dev2@company.com"],
    "group:dba": ["dba@company.com"]
  },
  "tagOwners": {
    "tag:k8s-server": ["group:devops"],
    "tag:database": ["group:devops"],
    "tag:grafana": ["group:devops"]
  }
}

This gives:

  • DevOps team: full access to K8s servers
  • Developers: only K8s API (port 443) and Grafana (port 3000)
  • DBAs: only database ports

No one gets blanket network access. Zero trust, enforced at the network level.

Step 6 — Expose Internal Tools with Funnel

Need to temporarily share an internal tool externally (for webhooks, demos, etc.)? Tailscale Funnel creates a public URL:

bash
# Expose local port 3000 publicly
tailscale funnel 3000

Output:

https://my-machine.tail1234.ts.net/
|-- proxy http://127.0.0.1:3000

This creates a public HTTPS URL backed by a valid TLS certificate. Perfect for:

  • GitHub/GitLab webhook testing
  • Sharing staging environments with clients
  • Demo environments

Turn it off when done — it stops immediately.

Step 7 — Integrate with CI/CD

GitHub Actions with Tailscale

Access private resources from your CI pipeline:

yaml
name: Deploy
on:
  push:
    branches: [main]
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: tailscale/github-action@v3
      with:
        oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
        oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
        tags: tag:ci
 
    - name: Deploy to K8s
      run: |
        kubectl --kubeconfig=$KUBECONFIG apply -f manifests/

The GitHub Actions runner joins your Tailscale network, accesses the K8s API server, deploys, and disconnects. No public API endpoint needed.

GitLab CI

yaml
deploy:
  image: ubuntu:22.04
  before_script:
    - curl -fsSL https://tailscale.com/install.sh | sh
    - tailscale up --auth-key=$TAILSCALE_AUTH_KEY --hostname=gitlab-runner
  script:
    - kubectl apply -f manifests/
  after_script:
    - tailscale down

Step 8 — Monitor Your Tailscale Network

Check the health of your network:

bash
# See all connected devices
tailscale status
 
# Check connection quality to a specific node
tailscale ping my-server
 
# View network diagnostic info
tailscale netcheck

Set up alerts for when critical nodes disconnect. Tailscale provides webhook notifications in the admin console for device connect/disconnect events.

Architecture Overview

Here's what your infrastructure looks like with Tailscale:

┌─────────────────────────────────────────────────┐
│                 Tailscale Network                │
│                 (WireGuard Mesh)                 │
│                                                  │
│   👨‍💻 Laptop ◄──────► 🖥️ K8s Control Plane     │
│        │                      │                  │
│        │              ┌───────┴───────┐          │
│        │              │  Worker Nodes  │          │
│        │              └───────────────┘          │
│        │                                         │
│        ├──────────► 📊 Grafana                   │
│        │                                         │
│        ├──────────► 🗄️ Database (via subnet)     │
│        │                                         │
│   🔄 CI Runner ◄──► 🖥️ K8s (deploy access)     │
└─────────────────────────────────────────────────┘

Internet: NOTHING exposed publicly

Wrapping Up

Tailscale replaces the complexity of traditional VPNs with a 5-minute setup that's more secure and faster. For DevOps teams, it means:

  1. Kubernetes API servers never need public endpoints
  2. Databases stay completely private
  3. Internal tools are accessible without port forwarding
  4. CI/CD pipelines access private resources securely
  5. Access control is identity-based, not network-based

Start with installing Tailscale on your laptop and one server. Once you see how easy it is, you'll want it everywhere.

For hands-on practice with Kubernetes networking and security, KodeKloud's courses cover network policies, ingress, and cluster security in depth. And if you need a cloud environment to test this setup, DigitalOcean droplets are perfect for spinning up test servers with Tailscale.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments