SPIFFE & SPIRE Cheatsheet
Operational reference for SPIFFE workload identity and SPIRE deployment. SPIFFE IDs, SVID issuance, registration entries, attestor selectors, federation, and the diagnostic commands you reach for when a workload is not getting its identity.
SPIFFE ID structure
4 commandsspiffe://<trust-domain>/<path>Canonical SPIFFE ID format. Trust domain is your organisation; path identifies the workload.
Production note: Use a hierarchical path that mirrors deployment scope: /ns/<namespace>/sa/<service-account> on Kubernetes.
spiffe://corp.example.com/ns/payments/sa/payments-apiProduction-style SPIFFE ID. Encodes cluster, namespace, and service account in the path so authz can prefix-match.
Production note: Pick the trust domain once; changing it later requires re-issuing every SVID across the fleet.
X.509 SVIDX.509 certificate whose URI SAN is the SPIFFE ID. Used for mTLS handshakes.
Production note: Default TTL in SPIRE is 1 hour with rotation at half-life. Tune via -ttl on the registration entry.
JWT SVIDSigned JWT with the SPIFFE ID in the `sub` claim. For HTTP/gRPC services that cannot terminate TLS themselves.
Warning: JWT SVIDs are bearer tokens — anyone who steals them can impersonate the workload until they expire. Prefer X.509 mTLS where possible.
SPIRE Server: cluster bootstrap
4 commandsspire-server run -config server.confStart the SPIRE Server with the given config file. Server holds the trust domain CA and issues SVIDs.
Production note: Run as a StatefulSet on Kubernetes with persistent storage for the datastore. HA needs an external SQL backend.
spire-server token generate -spiffeID spiffe://corp.example.com/spire/agent/<id>Generate a one-time join token to bootstrap a SPIRE Agent. Use only when no native attestation method is available.
Warning: Join tokens are bearer secrets. Prefer node-level attestation (k8s_psat, aws_iid, gcp_iit, k8s_sat) in production.
spire-server bundle showPrint the trust bundle (CA chain) for this trust domain. Federate it to peer trust domains.
Production note: Combine with the Bundle Endpoint protocol (`-format pem` for static seeding).
spire-server healthcheckLiveness probe. Exits 0 when the server is ready to serve.
Production note: Wire into Kubernetes liveness/readiness probes. Combine with prometheus_metrics for SLOs.
SPIRE Agent: workload attestation
4 commandsspire-agent run -config agent.confStart the SPIRE Agent on a node. Attests workloads via local OS or Kubernetes signals and proxies the Workload API.
Production note: Deploy as a DaemonSet; mount /run/spire/agent.sock as the Workload API endpoint for pods.
spire-agent api fetch x509Fetch the current X.509 SVID for the workload calling the Workload API. Used for diagnostics.
Production note: In code, use the go-spiffe SDK (workloadapi.NewX509Source) so rotation is automatic.
spire-agent api fetch jwt -audience https://api.example.comMint a JWT SVID with the given audience. Audience is the receiving service URL.
Warning: Always validate `aud` on the receiving end — a JWT minted for service A must not be accepted by service B.
spire-agent healthcheckVerify the agent has registered with the server and is serving the Workload API.
Production note: If agent healthcheck passes but workloads cannot fetch SVIDs, the registration entries are missing or the selectors do not match.
Registration entries (workload identity assignment)
4 commandsspire-server entry create -spiffeID <id> -parentID <parent> -selector <key:val>Create a registration entry. The agent issues the SVID to any workload that matches all selectors AND descends from the parent.
Production note: Combine multiple selectors (k8s:ns + k8s:sa + k8s:image-id) so identity binds to the specific deployment, not just the namespace.
spire-server entry listShow all registration entries on this server. Pipe through grep to find the entry for a specific SPIFFE ID.
Production note: In production, manage entries declaratively with the SPIRE Controller Manager (CRDs ClusterSPIFFEID + ClusterFederatedTrustDomain) instead of imperative CLI.
spire-server entry update -entryID <id> -ttl 600Update an existing entry. -ttl sets SVID validity in seconds (default 3600).
Production note: Shorter TTLs reduce blast radius of leaked SVIDs but increase load on SPIRE Server. 600–3600s is typical for production.
spire-server entry delete -entryID <id>Delete a registration entry. Workload loses identity at next refresh.
Warning: Plan deletes carefully — services using the SVID will fail TLS handshakes within minutes.
Attestor selectors (Kubernetes Workload Attestor)
5 commandsk8s:ns:<namespace>Match pods in a specific Kubernetes namespace.
Warning: Single-selector entries on namespace alone match every pod in the namespace — a misplaced pod gets the SVID.
k8s:sa:<service-account>Match pods running under a specific ServiceAccount.
Production note: Combine with k8s:ns: for tenant + role scoping.
k8s:pod-label:<key>:<value>Match pods with a specific label.
Production note: Use for "app=payments-api" style identity binding when ServiceAccounts are shared.
k8s:container-image:<image>Match pods running a specific container image (by digest).
Production note: Strongest selector — binds identity to the exact image hash. Combine with k8s:ns: and k8s:sa: for defence in depth.
unix:uid:<uid> / unix:path:<path>Unix Workload Attestor selectors. Match the calling process by UID, path, or SHA256.
Production note: Use for VM / non-Kubernetes workloads. unix:sha256:<digest> binds identity to the specific binary.
Federation (cross trust domain)
3 commandsspire-server federation create -trustDomain peer.example.com -bundleEndpointURL https://peer.example.com:8443 -bundleEndpointProfile https_webFederate with another trust domain. SPIRE will fetch the peer's trust bundle dynamically over the bundle endpoint.
Production note: https_web uses public CA validation for the bundle endpoint (simplest). https_spiffe uses a SPIFFE ID for endpoint authentication (most secure).
spire-server federation listShow configured federation relationships.
Production note: Workloads can authenticate peers in any federated trust domain — combine with authz policy that allowlists the peer SPIFFE IDs you trust.
spire-server bundle set -id spiffe://peer.example.com -path peer-bundle.pemStatic seed of a peer trust bundle. One-time bootstrap before bundle endpoint takes over.
Warning: Static bundles go stale. Always pair with bundle endpoint for ongoing rotation.
Diagnostics ("my workload cannot get an SVID")
4 commandsspire-server entry show -spiffeID spiffe://corp.example.com/ns/payments/sa/payments-apiConfirm the registration entry exists with the expected selectors.
Production note: If empty: the entry was never created (most common cause).
kubectl logs -n spire spire-agent-xxx | grep "Workload"Tail the SPIRE Agent on the node where the workload is running. Look for "Workload not registered" or selector-mismatch messages.
Production note: Run on the right node — DaemonSet means there is one agent per node.
spire-agent api fetch x509 -socketPath /run/spire/agent.sockFrom inside the workload pod (or via kubectl exec), call the Workload API directly to see what (if anything) the agent issues.
Production note: If empty here but entry exists: selectors do not match. Compare actual pod labels/SA to what the entry expects.
spire-server entry list -selector k8s:ns:paymentsList all entries scoped to a namespace. Useful when entries were created with overly broad selectors.
Warning: If an entry matches more workloads than intended, every match gets the same SVID — review selectors carefully.
Common misconfigurations
The unsafe pattern, the replacement, and the reason the two are not equivalent in production.
Risky
spire-server entry create \
-spiffeID spiffe://example.com/payments-api \
-selector k8s:ns:paymentsHardened
spire-server entry create \
-spiffeID spiffe://example.com/ns/payments/sa/payments-api \
-selector k8s:ns:payments \
-selector k8s:sa:payments-api \
-selector k8s:container-image:registry/payments-api@sha256:abc...Why it matters: A single namespace selector matches every pod in the namespace. Combining ns + ServiceAccount + image-digest binds identity to the specific deployment, so a misplaced or compromised pod cannot acquire an unintended SVID.
Risky
// Read SVID once at startup, never refresh:
src, _ := workloadapi.NewX509Source(ctx)
cert := src.GetX509SVID()
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert.Certificate()},
}Hardened
// Use the live source for every handshake:
src, _ := workloadapi.NewX509Source(ctx)
tlsConfig := tlsconfig.MTLSServerConfig(src, src,
tlsconfig.AuthorizeAny())Why it matters: GetX509SVID() returns a snapshot; the tlsconfig helpers install GetCertificate / GetClientCertificate callbacks that read from the live source on each handshake. Without that, SVID rotation has no effect — the server keeps presenting the cert it captured at startup.
Risky
// Static bundle seed only:
spire-server bundle set \
-id spiffe://us-west.example.com \
-path us-west-bundle-2024-Q3.pem
# (re-applied manually every quarter)Hardened
spire-server federation create \
-trustDomain us-west.example.com \
-bundleEndpointURL https://spire.us-west.example.com:8443 \
-bundleEndpointProfile https_spiffe \
-endpointSpiffeID spiffe://us-west.example.com/spire/serverWhy it matters: Static bundle copies go stale on rotation. The bundle endpoint protocol fetches the federated trust bundle dynamically (default every 5 minutes), so a CA rotation propagates within minutes — no manual ops, no 3am incident when the upstream CA rolls.
Related learning paths
Mastering SPIFFE & SPIRE course
Free 13-module curriculum that takes you from "what is workload identity" to running a federated SPIRE deployment.
ContinueZero Trust Network Builder simulator
Interactive SPIFFE/SPIRE design challenges — six production scenarios, four-choice format.
ContinueRun SPIRE on Kubernetes
Module: deploy SPIRE Server + Agent on Kubernetes from scratch.
ContinueWorking with SVIDs (Workload API)
Module: SVID issuance, rotation, and the SDK patterns that survive production.
Continue