Part IV of the Understanding Basic Kubernetes Concepts series, updated from the 2016 original.
When I wrote about Secrets and ConfigMaps almost ten years ago, the post was a quick primer. Two primitives, two paragraphs of explanation each, a closing note on portable containers. Re-reading it now, the two objects I described are still functionally the same. What I would write differently is everything around them.
That's the observation worth opening with: the primitives didn't move in a decade. Everything that produces, stores, and authenticates against them did. The Secret object, the thing the 2016 post taught you to put first, is now the last mile of a longer pipeline rather than the source of truth at its head. If you only carry the 2016 mental model (write a Secret manifest, kubectl apply, done), you'll get production wrong in 2026 in some specific and avoidable ways.
So this is two things at once. A refresher on what Secret and ConfigMap objects still are, because the search intent that brings people here hasn't shifted. And a survey of what now sits upstream of the last mile, because that's where the interesting decisions live.
The reason these two objects exist hasn't changed. The third factor of the 12-factor app methodology, store config in the environment, is still right. Configuration belongs outside the container image because the image should be portable across dev, staging, and production, and because sensitive material has no business in a registry, even a private one.
A Secret is the Kubernetes object for sensitive data: small (under 1 MiB), namespace-scoped, mountable as files in a volume or as environment variables in a pod. Each container that needs a Secret has to request it explicitly; there's no implicit sharing inside the pod. There's a special flavour, imagePullSecrets, that passes registry credentials to the kubelet so it can pull private images.
A ConfigMap is the same shape, but for non-sensitive data: strings, key-value pairs, full config files, JSON blobs, delivered the same ways. One behavioural difference worth knowing: a ConfigMap projected as a volume gets updated in running pods without a restart, depending on how the container consumes it. Secrets don't; you need a rollout to pick up changes.
So far, so 2016. Both are still namespace-scoped, both still cached on nodes only where they're needed.
Here is the line my original post understated: Kubernetes Secrets are not encrypted by default. They are base64-encoded, which is not the same thing. That matters more in 2026 than the 2016 post let on, and I'll come back to it.
The biggest shift in how teams use these objects has been quiet. The Secret manifest in your repo, applied with kubectl, is no longer the canonical version. It's a projection.
The pattern we see most often: an external secret manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, GCP Secret Manager) holds the actual material, and a controller in the cluster syncs it into native Kubernetes Secret objects. The K8s Secret becomes a cache. The source of truth lives somewhere with rotation policies, audit logs, and a separation of duties the cluster doesn't have on its own.
The de facto choice for this is the External Secrets Operator (ESO). Two custom resources do the work: a SecretStore (or ClusterSecretStore) defines how to authenticate to the external system, and an ExternalSecret defines what to fetch and how often. ESO's external-secrets.io/v1 API reached general availability in 2024. It's not the only option, but it's the one I'd reach for first when an external secret manager is already in the picture.
The GitOps-native alternatives are different in kind. Sealed Secrets lets you keep encrypted Secret manifests in Git directly: a cluster-side controller decrypts them on the way in. SOPS, particularly with Flux's native integration, encrypts the secrets in your repo files using KMS, age, or PGP, and decrypts at apply-time.
These aren't interchangeable. ESO assumes you have a serious external secret manager and treats the K8s Secret as a cache. SOPS and sealed-secrets assume Git is your source of truth and the encrypted material lives there. There are good reasons for both. Pick based on where your secrets actually live today and what your audit story needs to look like, not on which one came up first in a conference talk.
The most common avoidable vulnerability I see in production clusters in 2026 is the one the 2016 post implicitly recommended: an AWS access key, an Azure service principal credential, or a GCP service account JSON file, mounted as a Secret. Easy to set up. Easy to forget. Easy, eventually, to commit to the wrong repo.
The 2026 answer is workload identity. The cluster proves which workload is calling, and the cloud trusts the proof. On AWS that's IRSA (IAM Roles for Service Accounts) or EKS Pod Identity. On Azure, it's Azure AD Workload Identity. On GCP, it's Workload Identity for GKE. Different names, same idea: short-lived, identity-scoped credentials issued at runtime, no long-lived material sitting in a Secret waiting to leak.
There are good reasons teams haven't fully migrated. Workload identity adds setup complexity, especially across multiple clusters or across clouds. For a small team running a single workload on a single cloud, the access-key-in-a-Secret pattern still works on day one. I understand it. But it should be a known transitional state, not a permanent one.
If you're staffing or auditing a platform team in 2026 and the answer to "where are our cloud credentials?" is "in a Secret in the namespace next to the app," that's a finding, not a feature.
Both of the moves above (external sources of truth, workload identity) only matter if the Secrets that do end up in the cluster are actually protected. Which brings me back to the line I flagged in the primitives section.
Secrets at rest in etcd are base64-encoded by default. That is not encryption. Anyone with read access to etcd, or anyone with cluster-admin, can decode them in one step. The 2026 minimum is encryption at rest via a KMS provider (AWS KMS, Azure Key Vault, GCP KMS, or Vault) configured through EncryptionConfiguration. The Kubernetes documentation is solid on this and the configuration is straightforward. If you're running production and you haven't enabled it, you should.
But here is the part I want to be honest about. Across the production clusters I see, the more common compromise pattern is not an attack on etcd. It is RBAC drift. Too many people have read access to too many namespaces, and the "secret" is one kubectl get secret -o yaml away from anyone who could plausibly have a reason to need something else in that namespace.
Encryption at rest is necessary. It is not sufficient. The namespace boundary, and the RBAC bindings that police it, are where most real-world Secret compromises actually get prevented, or fail to be prevented. If you have to pick one thing to invest a week of platform-team time in, I would pick the RBAC review before I pick rotating the KMS keys.
The shape of Kubernetes secrets management is still moving, and I think the next five years will move it further than the last ten did. Workload identity is becoming the default assumption rather than the advanced option. Agentic and AI workloads are pushing on how short-lived credentials should be. When an agent calls out to three services in a chain, the credential lifetimes start to look more like seconds than hours. SPIFFE and SPIRE are providing a cluster-agnostic identity layer that fits the same pattern.
What I'd tell my 2016 self, if I could send a note back: don't treat the Kubernetes Secret object as the answer. Treat it as the last mile. The interesting decisions are upstream, and the next refresh of this post probably won't be about Secrets at all. It'll be about how the systems around them made the primitive almost incidental.
Across the 150+ production clusters Giant Swarm operates, that's already the direction of travel. The Secret object is still there. Almost nothing important about a team's security posture is decided at that layer anymore.