πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Security intermediate ⏱ 15 minutes K8s 1.24+

Kubernetes Service Account Token Guide

Create and manage Kubernetes service account tokens. TokenRequest API, projected volumes, long-lived tokens, and RBAC binding for pod-to-API authentication.

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: Since Kubernetes 1.24, service account tokens are no longer auto-created as Secrets. Use kubectl create token <sa> for short-lived tokens or projected volumes for pod authentication. Long-lived tokens require explicit Secret creation.

The Problem

After Kubernetes 1.24:

  • Auto-generated Secret-based tokens were removed (KEP-2799)
  • Pods still need API authentication for controllers, operators, and sidecar access
  • External systems (CI/CD, monitoring) need tokens to access the cluster
  • Token rotation and expiry must be managed explicitly

The Solution

Short-Lived Token (TokenRequest API)

# Create a 1-hour token (default)
kubectl create token my-service-account

# Custom expiration (max depends on cluster config)
kubectl create token my-service-account --duration=24h

# For a specific namespace
kubectl create token my-service-account -n production

# With specific audience
kubectl create token my-service-account --audience=https://vault.example.com

Create a Service Account

# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-controller
  namespace: production
  annotations:
    description: "Used by app-controller deployment for API access"
automountServiceAccountToken: false  # Don't auto-mount in pods

Bind RBAC Permissions

# role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-controller-role
  namespace: production
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "patch"]
  - apiGroups: [""]
    resources: ["pods", "services"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-controller-binding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: app-controller
    namespace: production
roleRef:
  kind: Role
  name: app-controller-role
  apiGroup: rbac.authorization.k8s.io
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-controller
spec:
  template:
    spec:
      serviceAccountName: app-controller
      automountServiceAccountToken: false
      containers:
        - name: controller
          image: myapp:1.0.0
          volumeMounts:
            - name: sa-token
              mountPath: /var/run/secrets/kubernetes.io/serviceaccount
              readOnly: true
      volumes:
        - name: sa-token
          projected:
            sources:
              - serviceAccountToken:
                  path: token
                  expirationSeconds: 3600   # Auto-rotated before expiry
                  audience: "https://kubernetes.default.svc"
              - configMap:
                  name: kube-root-ca.crt
                  items:
                    - key: ca.crt
                      path: ca.crt
              - downwardAPI:
                  items:
                    - path: namespace
                      fieldRef:
                        fieldPath: metadata.namespace

Long-Lived Token (Legacy / External Use)

# long-lived-token.yaml
# Only use when TokenRequest API isn't an option
apiVersion: v1
kind: Secret
metadata:
  name: app-controller-token
  namespace: production
  annotations:
    kubernetes.io/service-account.name: app-controller
type: kubernetes.io/service-account-token
# Retrieve the token
kubectl get secret app-controller-token -n production -o jsonpath='{.data.token}' | base64 -d

Architecture

graph TD
    A[Service Account] --> B{Token Type}
    B -->|Short-lived| C[TokenRequest API]
    B -->|Pod auth| D[Projected Volume]
    B -->|External/Legacy| E[Secret-based Token]
    C --> F[kubectl create token]
    C --> G[1h default, auto-expires]
    D --> H[Auto-rotated by kubelet]
    D --> I[Bound to pod lifetime]
    E --> J[Never expires]
    E --> K[Must manually rotate]
    
    A --> L[RoleBinding]
    L --> M[Role/ClusterRole]
    M --> N[API Access]

Use Token from Application Code

# Python example β€” reading projected token
import requests
from pathlib import Path

TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"
CA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
NAMESPACE = Path("/var/run/secrets/kubernetes.io/serviceaccount/namespace").read_text()

token = Path(TOKEN_PATH).read_text()
api_url = "https://kubernetes.default.svc"

# List pods in current namespace
resp = requests.get(
    f"{api_url}/api/v1/namespaces/{NAMESPACE}/pods",
    headers={"Authorization": f"Bearer {token}"},
    verify=CA_PATH
)
print(resp.json()["items"])

Common Issues

IssueCauseFix
”no service account token” in podautomountServiceAccountToken: false without projected volumeAdd projected volume or set true
Token expired errorTokenRequest token expiredUse projected volume (auto-rotates)
403 ForbiddenMissing RBAC bindingCreate Role + RoleBinding for the SA
Secret not populatedSA doesn’t exist yetCreate ServiceAccount before Secret
Token works locally, fails in podWrong audience claimMatch audience in TokenRequest and API server

Best Practices

  1. Use projected volumes over Secret-based tokens β€” auto-rotated, bound to pod lifetime
  2. Set automountServiceAccountToken: false on ServiceAccount β€” opt-in per deployment
  3. Scope RBAC minimally β€” only the verbs and resources actually needed
  4. Avoid long-lived tokens β€” use TokenRequest API or OIDC federation for external access
  5. Set token expiration β€” expirationSeconds: 3600 in projected volumes

Key Takeaways

  • Kubernetes 1.24+ no longer auto-creates Secret-based tokens
  • kubectl create token generates short-lived tokens (default 1h)
  • Projected volumes are the recommended pod authentication method β€” auto-rotated by kubelet
  • Long-lived tokens still work via explicit Secret creation but should be avoided
  • Always pair service accounts with least-privilege RBAC bindings
#service-account #token #rbac #authentication #security #tokenrequest
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens