Kubernetes Secrets Are Base64, Not Encryption
No — a Kubernetes Secret's data is Base64-encoded, not encrypted. Base64 has no key and hides nothing, so anyone who can read the object can decode the value with one command: base64 --decode. Treat any Secret YAML as plaintext credentials.
The thing everyone misreads
If you've run Kubernetes for more than a day, you've seen this:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4=
password: c3VwZXJzZWNyZXQ=
And somewhere in the back of your mind you filed it under "encrypted credentials." It isn't. Those values are Base64, and Base64 is encoding, not encryption. YWRtaW4= is just admin written in a different alphabet — reversible instantly, by anyone, with no key. This trips up an astonishing number of teams, so let's clear it up for good.
Prove it in one command
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode
# supersecret
No key. No password. No "decryption." Base64 is a binary-to-text encoding — its entire job is to represent arbitrary bytes using a safe 64-character alphabet so they survive transport and storage in text-based systems (etcd, YAML, JSON, HTTP headers). Kubernetes encodes Secret data values purely so binary values (certs, keys, gzip blobs) can live inside a YAML/JSON object. That's it. Security was never the point.
If you'd rather eyeball a whole Secret at once instead of decoding fields one by one, the Kubernetes Secret Decoder does it locally in your browser — paste the YAML and it decodes every data: value client-side, with nothing uploaded.
data vs stringData
A quick related gotcha: data expects Base64, but stringData expects plain text and Kubernetes Base64-encodes it for you on write:
stringData:
password: supersecret # plain text; k8s encodes it into data.password
Both end up identically un-secret at rest.
So what actually protects a Secret?
Base64 gets you nothing here. Real protection is layered:
- Encryption at rest for etcd — configure a
KMSprovider (AWS/GCP/Azure KMS) or at minimumaescbc/secretboxvia anEncryptionConfiguration. Without this, Secrets sit in etcd Base64-only. - Sealed Secrets (Bitnami) — encrypt secrets before they hit Git; only the in-cluster controller can decrypt. Safe to commit.
- SOPS (+ age/KMS) — encrypt values in your manifests/GitOps repo.
- External secret stores — HashiCorp Vault, AWS Secrets Manager, etc., pulled in via the External Secrets Operator.
- RBAC — lock down who can
get/listSecrets. If a ServiceAccount can read the Secret, it can read the plaintext.
Encoding vs encryption vs hashing
These three transformations get lumped together but do fundamentally different jobs. The clearest way to tell them apart is to ask: does it need a key, can you reverse it, and does it actually hide anything? Kubernetes Secrets, out of the box, sit in the left column.
| Encoding (Base64) | Encryption (AES-GCM) | Hashing (SHA-256) | |
|---|---|---|---|
| Purpose | Safe transport of bytes | Confidentiality | Integrity / fingerprint |
| Needs a key? | No | Yes | No |
| Reversible? | Yes, trivially | Yes, with the key | No (one-way) |
| Hides data? | No | Yes | N/A |
Base64 is in the left column. Kubernetes Secrets, out of the box, are in the left column. More on that distinction: Is Base64 Encryption?
TL;DR
- A Kubernetes Secret's
datais Base64, not encrypted — decode it with onebase64 -d. - Treat any Secret YAML as plaintext credentials: don't commit it, lock down RBAC.
- For actual protection: encryption at rest (KMS), Sealed Secrets, SOPS, or an external secret store.
The next time someone says "it's fine, it's in a Secret," you'll know to ask the real question: is etcd encrypted, and who has RBAC to read it?
Frequently Asked Questions
Are Kubernetes Secrets encrypted?
Not by default. A Secret's data values are Base64-encoded, not encrypted. Base64 is a reversible encoding with no key, so anyone who can read the Secret object can decode the value instantly with a single base64 --decode. Encryption only happens if you configure encryption at rest for etcd.
How do I decode a Kubernetes Secret?
Run kubectl get secret NAME -o jsonpath='{.data.KEY}' | base64 --decode. No key or password is required because Base64 is an encoding, not encryption. You can also paste an entire Secret YAML into a client-side decoder like the Kubernetes Secret Decoder to decode every data value at once.
What is the difference between data and stringData in a Secret?
The data field expects values that are already Base64-encoded. The stringData field expects plain text, and Kubernetes Base64-encodes it for you on write, storing it under data. Both end up identically un-secret at rest — neither is encrypted.
If Base64 isn't encryption, how do I actually protect a Secret?
Use real protection layers: encryption at rest for etcd via a KMS provider or an EncryptionConfiguration, Sealed Secrets or SOPS to encrypt values before they reach Git, an external secret store like HashiCorp Vault or AWS Secrets Manager, and RBAC to restrict who can get or list Secrets.
Why does Kubernetes Base64-encode Secret values at all?
Base64 is a binary-to-text encoding. Kubernetes encodes Secret data values so that arbitrary binary content — TLS certificates, private keys, gzip blobs — can live safely inside a text-based YAML or JSON object and in etcd. The purpose is transport and storage safety, not security.
Decode a Kubernetes Secret in your browser
Paste a Secret YAML into the Kubernetes Secret Decoder and every data: value is decoded locally — nothing is uploaded.