Post

Tailscale

Tailscale

Tailscale

Tailscale deployed as an exit node and subnet router inside the k3s cluster, giving tailnet devices a path onto the home LAN and out to the internet through the cluster. Runs in its own tailscale namespace, separate from the rest of this workspace’s apps (which mostly share default).

Deployment

ItemValue
Namespacetailscale
Imagetailscale/tailscale:v1.102.3
NodePinned to cm4-left via nodeSelector
StrategyRecreate (single-writer PVC + single-node pin)
StoragePVC tailscale-state, 1Gi, storageClassName: longhorn (replicated — holds the tailnet node identity/keys)
AuthTS_AUTHKEY from Secret tailscale-auth

reloader.stakater.com/auto: "true" is set on the Deployment.

Role: Exit Node + Subnet Router

TS_EXTRA_ARGS advertises both capabilities:

1
--advertise-exit-node --advertise-routes=192.168.1.0/24

This lets other devices on the tailnet either route all their traffic through the cluster’s network (exit node) or just reach the 192.168.1.0/24 LAN (subnet router). Both need to be approved for the node in the Tailscale admin console before they take effect.

Kernel Networking Requirements

Running as an exit node/subnet router needs real kernel-level packet forwarding, which takes a few extra pieces compared to a plain tailnet client:

  • A privileged sysctl initContainer runs once at pod start to enable net.ipv4.ip_forward, net.ipv6.conf.all.forwarding, and net.ipv4.conf.all.src_valid_mark on the node’s network namespace.
  • TS_USERSPACE: "false" plus the NET_ADMIN / NET_RAW capabilities and a hostPath mount of /dev/net/tun put the container in kernel networking mode rather than Tailscale’s userspace-networking fallback, which can’t do exit-node/subnet-router forwarding.
  • TS_DEBUG_FIREWALL_MODE: "nftables" pins the nftables firewall backend instead of containerboot’s auto-detection.
  • TS_KUBE_SECRET: "" disables containerboot’s default behavior of mirroring state into a Kubernetes Secret — state is persisted to the tailscale-state PVC only (TS_STATE_DIR: /var/lib/tailscale).

GitOps

Managed by Argo CD (tailscale Application, K3S branch of the app’s own repo, auto-sync + self-heal + prune). The tailscale namespace itself ships as a plain namespace.yaml in that repo rather than being created via CreateNamespace: true, since the Argo CD Application’s sync options have that disabled.

This post is licensed under CC BY 4.0 by the author.