--- # RBAC: Tailscale needs to read/write its own state Secret apiVersion: v1 kind: ServiceAccount metadata: name: tailscale namespace: admin-system labels: app.kubernetes.io/instance: tailscale app.kubernetes.io/name: tailscale app.kubernetes.io/version: "1.94.1" --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: tailscale namespace: admin-system labels: app.kubernetes.io/instance: tailscale app.kubernetes.io/name: tailscale app.kubernetes.io/version: "1.94.1" rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: tailscale namespace: admin-system labels: app.kubernetes.io/instance: tailscale app.kubernetes.io/name: tailscale app.kubernetes.io/version: "1.94.1" roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: tailscale subjects: - kind: ServiceAccount name: tailscale namespace: admin-system --- # Persistent state so the node keeps its identity across restarts apiVersion: v1 kind: PersistentVolumeClaim metadata: name: tailscale-state namespace: admin-system labels: app.kubernetes.io/instance: tailscale app.kubernetes.io/name: tailscale spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 1Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: tailscale namespace: admin-system labels: app.kubernetes.io/instance: tailscale app.kubernetes.io/name: tailscale app.kubernetes.io/version: "1.94.1" spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app.kubernetes.io/instance: tailscale app.kubernetes.io/name: tailscale template: metadata: labels: app.kubernetes.io/instance: tailscale app.kubernetes.io/name: tailscale app.kubernetes.io/version: "1.94.1" annotations: match-regex.version-checker.io/tailscale: '^\d+\.\d+\.\d+$' spec: serviceAccountName: tailscale hostNetwork: true dnsPolicy: ClusterFirstWithHostNet containers: - name: tailscale image: tailscale/tailscale:v1.98.4 imagePullPolicy: IfNotPresent env: - name: TZ value: Europe/Budapest # Persist state on PVC so the node keeps its Tailscale identity - name: TS_STATE_DIR value: /var/lib/tailscale # Use kernel networking (not userspace) for full subnet routing support - name: TS_USERSPACE value: "false" # Auth key for initial registration (create at https://login.tailscale.com/admin/settings/keys) # Use a reusable, non-ephemeral key so the node persists - name: TS_AUTHKEY valueFrom: secretKeyRef: name: tailscale-secrets key: ts-authkey # Hostname shown in the Tailscale admin console - name: TS_HOSTNAME value: dooplex # Advertise the local network so Tailscale clients can reach all services - name: TS_ROUTES value: "192.168.0.0/24" # Accept MagicDNS for name resolution within the tailnet - name: TS_ACCEPT_DNS value: "true" # Store state in a Kubernetes secret as fallback - name: TS_KUBE_SECRET value: tailscale-state # Extra args: enable subnet router and accept routes from other nodes - name: TS_EXTRA_ARGS value: "--accept-routes" securityContext: capabilities: add: - NET_ADMIN - NET_RAW livenessProbe: exec: command: - tailscale - status - --json initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: exec: command: - tailscale - status - --json initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 resources: requests: cpu: 50m memory: 128Mi limits: cpu: 500m memory: 256Mi volumeMounts: - name: state mountPath: /var/lib/tailscale - name: tun mountPath: /dev/net/tun volumes: - name: state persistentVolumeClaim: claimName: tailscale-state - name: tun hostPath: path: /dev/net/tun type: CharDevice