Base64 Is Not Encryption: Kubernetes Secrets, Key Vault and Redis Cache Words Explained
▶ Watch on YouTube & subscribe to The Stack Underflow
Two deep dives in this series follow one Spring Boot bookstore on AKS. In one, the database password travels from Azure Key Vault into a pod. In the other, book 42 is cached in Redis so most reads skip the database. Both lean on words that are easy to mix up: a Secret that isn’t encrypted, a cache hit, a TTL. This primer gives you the words first, each with a memory peg.
The one-line version: a Kubernetes Secret is an envelope, not a lock, and a cache is a copy, not the truth.
Last verified against kubernetes.io, Microsoft Learn, redis.io and the Spring reference docs: 1 October 2026. The facts match the deep dives: Kubernetes 1.37 docs, the AKS Key Vault provider add-on with Microsoft Entra Workload ID, Spring Boot 4.1.1, Spring Framework 7.0.9, Spring Data Redis 4.1.1 and Azure Managed Redis.
Every word gets a peg from one of two pictures: a bank vault and its couriers for secrets, and a restaurant’s pass for caching. Pegs are memory aids (analogies), not facts; the text says where each one stops matching.
Secrets inside the cluster
| Word | Memory peg (analogy) | What it actually is |
|---|---|---|
| ConfigMap | A notice on the pinboard | An API object for non-confidential key-value settings. The Kubernetes docs say it gives no secrecy or encryption. |
| Kubernetes Secret | An envelope in the mailroom (base64 is not a lock) | An object for a small amount of sensitive data, such as a password, a token or a key |
| etcd, encryption at rest | The mailroom locked overnight | etcd is the key-value store behind the API server. Encryption at rest protects stored data, not API readers |
| Volume, mount | The pod’s locked drawer | A directory the pod’s containers can access; the mount is where it appears in a container |
| Environment variable | A note taped to the desk | Set from a Secret when the container starts, and not refreshed while it runs |
| Kubernetes RBAC | Who holds a key to the mailroom | A Role lists allowed verbs; a RoleBinding grants it. There are no deny rules |
Base64 is an encoding. In a manifest, Secret values are written in base64. The Kubernetes docs are explicit: base64 “is not an encryption method”, and checking such a manifest into a repository makes the secret available to everyone who can read it. Anyone who can read the manifest, or get the Secret through the API, decodes it in one command.
Where Secrets are stored. Upstream, Kubernetes stores Secrets unencrypted in etcd by default. On AKS, Azure always encrypts that storage at rest, and KMS can add a layer with a key from Key Vault. Where the peg breaks: locking the mailroom overnight stops someone carrying off the shelves, not a clerk with a key. API readers still get the plain value.
Files versus environment variables. Secret and CSI volumes live in tmpfs, in memory, and go away with the pod; anyone allowed to exec into the container can still read the files. A mounted file can be updated in place (not with subPath), but the app must read it again. An environment variable won’t see a change until the container restarts.
The key ring is bigger than it looks. RBAC that allows get, list or watch on Secrets reveals their contents. And anyone who can create a pod in a namespace can read the Secrets in that namespace, because the pod can mount them.
The bank vault and its couriers
| Word | Memory peg (analogy) | What it actually is |
|---|---|---|
| Azure Key Vault | The bank vault | Azure’s store for secrets, keys and certificates. A new value under the same name becomes a new version |
| Managed identity | An account at the bank | A Microsoft Entra identity for a workload; Azure manages its credentials, you never see them |
| Service account | The courier’s staff record | A non-human identity in the cluster. Pods get short-lived, automatically rotating tokens for it |
| Workload identity | The courier’s ID badge, checked by Entra ID at the bank’s front desk | Entra ID exchanges a trusted cluster token for an access token |
| Federated credential | The trust letter | ”Trust tokens from this issuer, for this subject, with this audience” |
| Secrets Store CSI driver | The courier | Mounts secrets from an external store into a pod as a volume, through a provider |
| Rotation | The courier restocks the drawer | Replacing a secret’s value regularly; in Key Vault the new value is a new version |
Access to the vault is Azure RBAC, not Kubernetes RBAC. The identity gets a role assignment on the vault, here Key Vault Secrets User, which Microsoft describes as “Read secret contents including secret portion of a certificate with private key”: it can read, not change. Don’t confuse the two RBACs: one guards the mailroom, the other the vault.
Proving who you are without a password. The cluster acts as a token issuer and publishes the public keys that check its tokens. Entra ID finds those keys through OIDC discovery, verifies the token’s signature, and checks issuer, subject and audience against the federated credential. If all match, it returns an access token. When a pod starts, the driver and the Azure provider on that node make this exchange, fetch the secret, and write it into the pod’s volume. If that fails, the pod waits in ContainerCreating.
Rotation is opt-in. On the AKS add-on, autorotation is off by default ("enableSecretRotation": "false"). Turned on, the provider polls for changes based on the rotation poll interval, two minutes by default, and updates the mounted files and any synced Kubernetes Secret. Where the peg breaks: the note taped to the desk is never restocked. Environment variables, and Spring’s cached config tree, need a restart.
Now the kitchen: cache words
| Word | Memory peg (analogy) | What it actually is |
|---|---|---|
| Cache | Ready dishes, kept close | Temporary copies of frequently accessed data, closer to the app than the source |
| Redis | The warming shelf by the pass | An in-memory data store; here it holds copies, not the truth |
| Database of record | The kitchen | Where the real value lives |
| Key and value | The label on the plate; the value is the dish | A unique key; a Redis string value is a sequence of bytes |
| Cache hit | The dish is already on the shelf | The stored copy is returned and your method never runs |
| Cache miss | Cook it, put a copy on the shelf | The method runs, queries the database, and the result is stored |
| Cache-aside | Check the shelf first | Read the cache; on a miss load and store; on a write update the store, then remove the copy |
Microsoft’s caching guidance says to cache data that’s read often and changed rarely, and not to use the cache as “the authoritative store of critical information”. On Azure Managed Redis, replication is asynchronous, so an unexpected failover can lose a little recently written data. Fine for copies, not for the truth.
What Spring does for you. Put @Cacheable("books") on findById(42) and a proxy runs cache-aside for you, under the Redis key books::42 (cache name, ::, the single parameter). Calls from inside the same class skip the proxy.
Keeping copies honest
| Word | Memory peg (analogy) | What it actually is |
|---|---|---|
| Serialization | Boxing the dish | Object to bytes before storing; bytes back to an object on a hit |
| TTL | The timer card on each plate | Time to live: when it runs out, the key expires |
| Eviction | Clearing old plates when the shelf is full | When memory reaches maxmemory, the eviction policy picks keys to remove |
Invalidation, @CacheEvict | Throwing a plate away: the recipe changed | Delete the copy after the data changes |
| Cache stampede | Everyone orders the missing dish | The common informal name for many requests missing the same key at once |
Serialization. Spring Data Redis’s default value serializer is Java serialization (JdkSerializationRedisSerializer); JSON serializers exist too. After a deploy changes the class, old boxes may not open.
TTL and expiry. Redis expires keys two ways: passively, when a client touches an expired key, and actively, by periodically testing a few random keys that have an expiration. The trap: Spring Boot’s Redis cache sets no TTL by default, so entries never expire. Set spring.cache.redis.time-to-live.
Eviction is not expiry. One follows memory pressure, the other a timer. Open-source Redis, by default, has no memory limit on 64-bit systems and the noeviction policy. Azure Managed Redis defaults to volatile-lru, which only evicts keys that have a TTL. Redis’s docs say the volatile-xxx policies “behave like noeviction if no keys have an associated expiration”: writes get an error, reads keep working.
Stale data. When book 42 changes in the database, the Redis copy doesn’t. @CacheEvict removes it after your update method succeeds. Update first, then evict; the other order lets a reader re-cache the old value.
Stampedes. By default Spring’s cache abstraction locks nothing, and Spring Data Redis’s default cache writer is lock-free. The deep dive shows the opt-in locking writer, and why its lock covers the whole cache.
Pause & Prove
1. Your database password is in a Kubernetes Secret, written in base64. Name three kinds of people who can read it.
- Anyone who can read the manifest. Base64 decodes in one command, so a manifest in a repository leaks the value.
- Anyone allowed to get, list or watch Secrets in that namespace. Those RBAC verbs reveal the contents.
- Anyone who can create a pod in the namespace. The pod can mount the Secret, so pod creators can read it.
Encryption at rest protects etcd’s storage, not API readers.
2. The values in a Kubernetes Secret’s manifest are base64. Base64 is…
- Encryption. Tempting, because the value looks scrambled. There is no key: anyone can decode it.
- An encoding. ✓ It changes the representation, not who can read it.
- A hash. A hash can’t be reversed; base64 can, in one command.
- A signature. A signature proves who produced data. Base64 proves nothing.
Upstream, Secrets are stored unencrypted in etcd by default; on AKS, Azure encrypts that storage at rest.
3. Peg drill
Say the peg before you look: Kubernetes Secret, workload identity, CSI driver, cache miss, TTL, @CacheEvict. (An envelope in the mailroom, base64 is not a lock · the courier’s ID badge · the courier · cook it, put a copy on the shelf · the timer card on each plate · throwing a plate away: the recipe changed.)
After this video
- How secrets reach a pod in AKS: the password’s journey from Key Vault into a pod, as a state machine, including why a pod gets stuck in
ContainerCreating. - Spring Boot Redis cache as a state machine: book 42 through hits, misses, writes, failures, expiry and eviction.
Sources
Defaults, dates and version-specific claims were re-checked on these pages on 1 October 2026; the ConfigMap, volume, RBAC, service account and CSI driver definitions come from the video’s own source check (29 September 2026):
- Kubernetes: Secrets: Secret definition, unencrypted in etcd by default, pod creators can read Secrets, subPath and tmpfs
- Kubernetes: Good practices for Secrets: base64 is not encryption, manifests in repositories
- Kubernetes: ConfigMaps, Volumes, RBAC, Service accounts: ConfigMap, volume, RBAC and service account definitions
- AKS: Key Vault provider for Secrets Store CSI Driver: add-on defaults (
enableSecretRotation: false,rotationPollInterval: 2m), subPath limitation - AKS: CSI driver configuration options: autorotation updates the mount and synced Secret; two-minute default interval; env vars need a restart
- AKS: Workload ID overview: cluster as token issuer, OIDC discovery of signing keys
- Key Vault: Azure RBAC guide: Key Vault Secrets User role
- Secrets Store CSI Driver: what the driver does
- Azure caching guidance: cache definition, not the authoritative store, asynchronous replication on Azure Managed Redis
- Cache-Aside pattern: load on demand, update the store before invalidating
- Redis: key eviction and EXPIRE: maxmemory default, policies, passive and active expiry
- Azure Managed Redis: memory management: default
volatile-lru - Spring Framework 7.0.9: cache annotations, Spring Boot 4.1.1: caching, Spring Data Redis 4.1.1: Redis cache: keys, sync,
@CacheEvict, defaults (no expiration, JDK serializer, lock-free writer)
Change notes
- 1 Oct 2026: first published.
Not affiliated with or endorsed by Microsoft, The Linux Foundation, Redis Ltd., or VMware Broadcom (Spring). Found a mistake? Tell us in the video’s comments and we’ll correct this page.
Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.
Subscribe on YouTube →Prefer email? Get the free newsletter: one failure, traced step by step, about once a week.