The GCP Secret Manager CSI driver lets you mount secrets directly into your GKE pods as files, eliminating the need for cumbersome ConfigMaps or manual secret injection.

Let’s see it in action. Imagine you have a Kubernetes deployment that needs a database password stored securely in Google Secret Manager.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app-container
        image: nginx:latest
        volumeMounts:
        - name: secrets-store-inline
          mountPath: "/mnt/secrets-store"
          readOnly: true
      volumes:
      - name: secrets-store-inline
        csi:
          driver: secrets-store.csi.k8s.io
          readOnly: true
          volumeAttributes:
            secretProviderClass: "gcp-secretmanager" # This links to our SecretProviderClass

Here’s the SecretProviderClass that tells the CSI driver which secrets to fetch and how to map them:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: gcp-secretmanager
spec:
  provider: gcp
  parameters:
    # Use 'project_id' if your secrets are in a different project than your GKE cluster.
    # project_id: "your-gcp-project-id"
    # If you are not using the default service account, specify the service account.
    # service_account: "your-gcp-service-account@your-gcp-project-id.iam.gserviceaccount.com"
    secrets: |
      - resourceName: "projects/YOUR_GCP_PROJECT_ID/secrets/MY_DATABASE_PASSWORD/versions/latest"
        alias: "db-password.txt" # This is the filename inside the pod
      - resourceName: "projects/YOUR_GCP_PROJECT_ID/secrets/MY_API_KEY/versions/latest"
        alias: "api-key.txt"

When this deployment starts, the secrets-store.csi.k8s.io driver, configured by SecretProviderClass gcp-secretmanager, will:

  1. Authenticate to GCP using the GKE node’s service account (or a specified one).
  2. Fetch the specified secrets (MY_DATABASE_PASSWORD and MY_API_KEY) from Secret Manager.
  3. Mount them as files within the /mnt/secrets-store directory inside the my-app-container. The contents of db-password.txt will be the value of MY_DATABASE_PASSWORD, and api-key.txt will be the value of MY_API_KEY.

This approach decouples your application from how secrets are managed. Your app just reads from a file, unaware of whether it came from a ConfigMap, a Kubernetes Secret, or GCP Secret Manager.

The problem this solves is primarily security and operational overhead. Traditional methods involve pulling secrets into Kubernetes, which can be a security risk if not managed perfectly (e.g., secrets stored in Git, exposed in logs, or accessible by unauthorized users within the cluster). With the CSI driver, secrets never leave GCP Secret Manager unless explicitly requested by the authenticated CSI driver, and they are only exposed within the pod’s filesystem. This also simplifies CI/CD pipelines, as you don’t need complex steps to inject secrets into Kubernetes objects.

The core mechanism relies on the Kubernetes CSI (Container Storage Interface) standard. The secrets-store.csi.k8s.io driver acts as a proxy. It intercepts volume mounts that specify it as the driver. It then consults the SecretProviderClass to understand which cloud provider (GCP in this case) to talk to, the specific secrets to retrieve, and how to present them (as files with specific names). The GCP provider plugin within the CSI driver handles the actual API calls to Google Secret Manager.

The surprising part is how seamlessly it integrates. You don’t need to build custom sidecars or complex init containers to fetch secrets. The Kubernetes scheduler and Kubelet handle the mounting of the "volume" just like any other storage volume. Your application code simply reads from a file path. It’s a declarative approach to secret injection that feels native to Kubernetes.

The resourceName in the SecretProviderClass can be a specific version (versions/1, versions/2) or versions/latest. If you use versions/latest, the CSI driver will periodically poll Secret Manager for updates. However, the frequency of this polling is not directly configurable in the SecretProviderClass itself. It’s typically managed by the CSI driver’s internal sync interval, which defaults to 10 minutes. If you need near real-time secret updates, you’d need to consider alternative strategies or potentially a custom controller, but for most use cases, the default polling is sufficient.

Once you have the secrets mounted as files, the next logical step is to ensure your application is robust to secret rotation, which involves understanding how the CSI driver handles updates and reloads.

Want structured learning?

Take the full Gke course →