Microservices often communicate over internal networks, and many production systems need more than simple network connectivity. A service may need to prove its identity to another service before the connection is trusted.

Mutual TLS (mTLS) is one common way to achieve this. Instead of relying only on a bearer token or network location, both sides can authenticate using X.509 certificates.

Kubernetes 1.37 makes this easier by graduating Pod certificates and ClusterTrustBundles to Stable. Pods can receive private keys and X.509 certificate chains through projected volumes, while ClusterTrustBundles provide a Kubernetes-native mechanism for distributing trust anchors.

For .NET developers, this creates a useful foundation for certificate-based workload identity without requiring application code to generate or manually manage certificates.

What Are Kubernetes Pod Certificates?

A Pod certificate allows a workload to receive a private key and certificate chain through a projected volume.

The high-level flow is:

.NET Pod
   |
   | Requests workload certificate
   v
Kubernetes API / Kubelet
   |
   v
Configured Signer
   |
   v
X.509 Certificate
   |
   v
Projected Volume
   |
   v
.NET Application

The application reads the certificate from the filesystem and uses it for TLS authentication.

Kubernetes handles certificate provisioning and refresh. The application is responsible for noticing certificate changes and reloading its credentials appropriately.

This is an important distinction: Kubernetes manages the lifecycle of the credential, while the application needs to consume that credential correctly.

Why Workload Identity Matters

Traditional applications often use one of several authentication approaches:

Service A
   |
   +-- API Key
   +-- Shared Secret
   +-- Bearer Token
   +-- Client Certificate

Shared secrets can be difficult to rotate safely. Long-lived credentials can also create unnecessary risk if they are copied between environments.

With certificate-based workload identity:

Pod A
  |
  | Client Certificate
  v
Pod B
  |
  | Validate certificate
  v
Trusted Identity

The certificate becomes part of the workload's cryptographic identity.

Kubernetes introduced Pod certificates specifically to provide workloads with a mechanism for obtaining certificates through the kubelet, including automated refresh.

Pod Certificates vs ServiceAccount Tokens

ServiceAccount tokens and Pod certificates solve different problems.

FeatureServiceAccount TokenPod Certificate
Credential typeTokenX.509 certificate
Primary useKubernetes API authenticationCertificate-based workload identity
mTLS supportNot directlyYes
Private keyNoYes
Certificate rotationNot applicableKubelet-managed
Useful for TLS clientsLimitedStrong fit
Requires signerNoYes

A ServiceAccount token remains useful for applications that need to authenticate to the Kubernetes API.

A Pod certificate is more appropriate when the application needs certificate-based identity for TLS or mTLS.

These mechanisms can therefore coexist.

How Pod Certificate Projection Works

A Pod requests a certificate by using a projected volume.

A simplified configuration looks like this:

apiVersion: v1
kind: Pod
metadata:
  name: orders-api
spec:
  serviceAccountName: orders-api
  containers:
    - name: orders-api
      image: example/orders-api:latest
      volumeMounts:
        - name: workload-identity
          mountPath: /var/run/workload-identity
          readOnly: true

  volumes:
    - name: workload-identity
      projected:
        sources:
          - podCertificate:
              signerName: example.com/workload
              keyType: ED25519
              credentialBundlePath: credentialbundle.pem

The important configuration is:

podCertificate:
  signerName: example.com/workload

The signerName identifies the signer that is expected to issue the certificate.

Kubernetes does not automatically make every arbitrary signer available. A signer controller must be deployed and configured to issue certificates for eligible Pods.

Why credentialBundlePath Is Useful

Kubernetes supports writing the private key and certificate chain separately, but it also supports a credential bundle.

For example:

credentialBundlePath: credentialbundle.pem

This is useful because the private key and certificate can rotate independently.

If an application reads:

private-key.pem
certificate.pem

as separate files, there is a possibility of reading the files during rotation and obtaining a mismatched key and certificate.

Kubernetes recommends using credentialBundlePath for applications that want to read the credentials as one consistent bundle.

Reading the Certificate in a .NET Application

A .NET application can load the certificate from the mounted filesystem.

For example:

using System.Security.Cryptography.X509Certificates;

var certificatePath =
    "/var/run/workload-identity/credentialbundle.pem";

var certificate =
    X509Certificate2.CreateFromPemFile(certificatePath);

However, a projected credential bundle contains both the private key and certificate chain, so the exact loading approach should match the PEM structure and certificate usage required by the application.

A more explicit approach for a PEM certificate and private key is:

using System.Security.Cryptography.X509Certificates;

var certificate =
    X509Certificate2.CreateFromPemFile(
        "/var/run/workload-identity/certificate.pem",
        "/var/run/workload-identity/private-key.pem");

The application should also ensure that the certificate and private key belong together.

For production systems, certificate loading should be encapsulated rather than scattered throughout controllers and service classes.

Using a Client Certificate With HttpClient

Suppose a .NET service needs to call another internal service using mTLS.

The application can configure an HttpClientHandler with a client certificate:

using System.Net.Http;
using System.Security.Cryptography.X509Certificates;

var certificate =
    X509Certificate2.CreateFromPemFile(
        "/var/run/workload-identity/certificate.pem",
        "/var/run/workload-identity/private-key.pem");

var handler = new HttpClientHandler();

handler.ClientCertificates.Add(certificate);

using var client = new HttpClient(handler);

var response =
    await client.GetAsync(
        "https://payments.internal/api/orders");

The receiving service must trust the certificate's issuer and validate the client certificate according to its own authentication policy.

The certificate alone does not automatically establish trust.

What Is a ClusterTrustBundle?

A certificate is useful only when the receiving system can determine whether it trusts the certificate issuer.

This is where ClusterTrustBundle fits.

A ClusterTrustBundle is a cluster-scoped object that distributes X.509 trust anchors to workloads. Kubernetes provides a projected volume mechanism so Pods can consume those trust anchors.

The architecture becomes:

Certificate
     |
     | Issued by
     v
Workload CA
     |
     | Trusted through
     v
ClusterTrustBundle
     |
     v
.NET Service

This separates two concepts:

Identity      → Pod certificate
Trust         → ClusterTrustBundle

That separation is useful when designing a larger internal PKI architecture.

Configuring a Trust Bundle

A Pod can project a ClusterTrustBundle alongside its certificate.

Conceptually:

volumes:
  - name: workload-identity
    projected:
      sources:
        - podCertificate:
            signerName: example.com/workload
            keyType: ED25519
            credentialBundlePath: credentialbundle.pem

        - clusterTrustBundle:
            signerName: example.com/workload
            path: ca-bundle.pem

The exact trust-bundle selection depends on how the signer and trust bundle are configured.

Kubernetes supports signer-linked and signer-unlinked trust bundles, with access controlled through Kubernetes authorization mechanisms.

Certificate Rotation in .NET

One of the most important production considerations is rotation.

The kubelet refreshes projected Pod certificate credentials as they approach expiration. The application must reload the updated credentials when the mounted files change.

A common mistake is loading the certificate once when the application starts:

var certificate =
    LoadCertificate();

var handler =
    CreateHandler(certificate);

and then keeping that certificate indefinitely.

That can cause problems after certificate rotation.

A better architecture is to place certificate loading behind a component that can detect credential changes and refresh the HTTP/TLS client configuration when necessary.

The exact implementation depends on whether the application uses HttpClient, ASP.NET Core Kestrel, gRPC, or another networking stack.

A Simple Certificate Provider

For example:

public interface IWorkloadCertificateProvider
{
    X509Certificate2 GetCertificate();
}

An implementation can encapsulate certificate loading:

public sealed class WorkloadCertificateProvider
    : IWorkloadCertificateProvider
{
    private readonly string _certificatePath;
    private readonly string _privateKeyPath;

    public WorkloadCertificateProvider(
        string certificatePath,
        string privateKeyPath)
    {
        _certificatePath = certificatePath;
        _privateKeyPath = privateKeyPath;
    }

    public X509Certificate2 GetCertificate()
    {
        return X509Certificate2.CreateFromPemFile(
            _certificatePath,
            _privateKeyPath);
    }
}

In a production implementation, this provider would also need a strategy for detecting file changes, replacing the certificate safely, and disposing of credentials that are no longer used.

Common Mistakes

Assuming a Certificate Is Automatically Trusted

Receiving a certificate does not mean another service trusts it.

The receiving application must trust the certificate's issuing CA or configured trust anchor.

Loading the Certificate Only Once

Certificates can rotate. An application that never reloads them can eventually attempt to use an expired credential.

Reading Key and Certificate Separately During Rotation

This can create a temporary mismatch between the private key and certificate.

Using credentialBundlePath provides a consistent bundle for atomic reads.

Using a Generic Signer Without Reviewing Its Policy

The signer controls which certificates it issues and under what conditions.

The Kubernetes documentation explicitly notes that signers can impose their own access requirements and may refuse certificate requests.

Treating Workload Identity as Authorization

A certificate establishes cryptographic identity. It does not automatically define what the identity is allowed to do.

Authorization still needs to be designed separately.

Troubleshooting Pod Certificates

Check the Pod

kubectl describe pod orders-api

Look for volume and mount errors.

Check the Projected Files

Inside the container:

ls -la /var/run/workload-identity

You should see the projected credential files.

Check the Certificate

openssl x509 \
  -in /var/run/workload-identity/certificate.pem \
  -text \
  -noout

Check:

Check the Signer

If a certificate is not being issued, verify that the configured signer controller is running and that it is authorized to issue certificates for the workload.

Check Rotation

If authentication works initially but fails later, inspect whether the application reloads the updated certificate after the projected files change.

Best Practices

  1. Use short-lived workload certificates where appropriate.

  2. Keep private keys inside the projected credential volume and avoid copying them elsewhere.

  3. Use a dedicated signer for workload identity.

  4. Define clear certificate issuance policies.

  5. Distribute trust anchors through ClusterTrustBundles where appropriate.

  6. Use credentialBundlePath when the application needs a consistent certificate/key pair.

  7. Make .NET applications capable of reloading rotated credentials.

  8. Separate authentication from authorization.

  9. Monitor certificate expiration and issuance failures.

  10. Test certificate rotation and signer failure before production deployment.

Advantages and Disadvantages

Advantages

Disadvantages

Conclusion

Kubernetes 1.37's graduation of Pod certificates and ClusterTrustBundles to Stable provides a stronger foundation for certificate-based workload identity. Pods can receive private keys and X.509 certificate chains through projected volumes, while trust bundles can distribute the CA information required to validate those identities.

For .NET services, the practical architecture is straightforward:

.NET Pod
   |
   +--> Pod Certificate
   |       |
   |       +--> Client identity
   |
   +--> ClusterTrustBundle
           |
           +--> Trusted CA

The most important production consideration is lifecycle management. The application must correctly consume and reload certificates as Kubernetes rotates them.

When implemented carefully, Pod certificates provide a clean foundation for mTLS and workload identity while keeping certificate provisioning and rotation closer to the Kubernetes platform instead of embedding credential-management logic throughout individual .NET services.