Secrets management — study guide
The concept's fragments, read in order.
Some values you must never leak
Most configuration is boring on purpose: a port number, a log level, the address of a database. Secrets are the configuration that is not boring. A password, an API key, an access token, a private key — these are the values that grant access, and anyone who holds one can do whatever it authorizes. That is the whole difference. Ordinary config tells a program how to behave; a secret lets it, or whoever stole it, act as someone trusted.
Because a secret is a credential rather than a setting, it needs handling ordinary config does not. The twelve-factor test for whether something is a secret is blunt: could the codebase be made open source at any moment without compromising any credentials? A database host can survive being published. A database password cannot. Anything that fails that test belongs in a category apart, kept out of the places where ordinary config is content to sit.
The rest of this concept is the handling that category demands: why a secret must never enter the repository, how to keep it out of the source entirely, where to store it instead, how to give each user and service only the secrets it needs, and why secrets are made to expire. Every one of these is a habit built around a single fact — a secret is only useful because it grants access, which is exactly why a leaked one is dangerous.
The one rule: not in the repo
There is one rule that comes before all the others: never commit a secret. Git history is permanent and, once pushed, shared. A commit does not vanish when a later commit changes the file — it stays in the history, reachable by its hash, and it travels into every clone and every fork anyone has made. Deleting the secret in a new commit removes it from the current version of the file and from nothing else.
This is why erasing is not fixing. like posting a photo of your house key online - taking the photo down later does not un-copy it, so the only real fix is to change the lock Once a secret has been committed and pushed, you have to assume it is already copied, and no amount of rewriting the visible history can un-copy it. The only response that actually restores safety is to rotate the secret: revoke the leaked credential and issue a new one, so the exposed value grants access to nothing. Cleaning up the history is worth doing afterward, but rotation is the step that ends the exposure.
So the rule is not really about tidiness; it is about the fact that a repository is the wrong shape for a secret. History that never forgets is a virtue for code and a liability for credentials. The safe move is to keep the secret out of the commit in the first place, which is the subject of everything that follows.
Out of the source, into the environment
If a secret must not enter the repository, it also must not sit in the source code, because source code is what the repository holds. Hardcoding a key into a file is committing it a moment later by accident. The fix is to make the code and the secret travel separately: the code carries the logic, and the secret is supplied to it at runtime from somewhere outside the repo.
The twelve-factor approach is to store this kind of config in environment variables. The program reads the value from its environment when it starts, and the value itself lives nowhere in the tracked files — there is little chance of an environment variable being checked into the repo by accident, precisely because it is not a file in the tree. A config file kept outside the repository works on the same principle, as long as it is genuinely outside and stays that way, which is what a .gitignore entry is for.
The point of both is separation. The code can be read, reviewed, and shared freely because it names the secret without containing it: it asks for DATABASE_PASSWORD, and what fills that in arrives from the environment it runs in. Where that value comes from, and how it is guarded before it gets there, is what a secret manager is for.
A vault that hands them out
Keeping a secret out of the code raises an obvious question: then where does it live? A plain file full of credentials on a shared machine is only a slower version of the problem — anyone who can read the file has every secret in it. A secret manager is the purpose-built answer. It stores secrets encrypted, so the stored form protects their confidentiality even at rest, and it becomes the one place secrets belong instead of scattered across files and machines.
What makes it more than encrypted storage is what it does at the moment a secret is requested. The manager gates access: it authenticates whoever is asking and checks whether that identity is allowed this particular secret before it releases anything. It audits, recording who requested a secret, whether the request was granted or refused, and when the value was used. And it delivers the secret to the application at runtime, so the value passes into a running process rather than sitting in a file someone can copy.
The shape this creates is worth holding onto: the secret never lives in the code, and every read is a decision the manager makes and remembers. Access can be revoked, examined after the fact, and reasoned about — none of which is possible for a credential pasted into a file. The vault turns a secret from an object you hope no one copies into a service you can actually control.
Only the keys it needs
Having a place that hands out secrets makes it tempting to let everything reach everything. Resist it. The rule is least privilege: each person and each service should get only the specific secrets it actually needs, and nothing more. No principal should be able to read all secrets at once, and the fine-grained access policies a secret manager offers exist precisely so that a given identity can be scoped down to the handful it requires.
The reason is blast radius. like giving each worker only the key to the one door they need, never the master key, so a lost key opens as little as possible Every credential an identity can reach is a credential that leaks when that identity is compromised. A service that can read only its own database password exposes exactly that if it is breached; a service holding the master set of every secret turns one compromise into a full-scale disaster. Narrow scope does not prevent the breach, but it decides how far the breach reaches.
This is the same least-privilege lens that governs permissions everywhere — grant the minimum that lets the work happen, and no convenience is worth a wider grant. Secrets are where the principle bites hardest, because a secret is pure access with nothing else attached. Scoping each one to the smallest audience that needs it is the difference between losing a key and losing every key.
Secrets expire on purpose
A secret that never changes is a secret that stays dangerous forever. The longer a credential is valid, the more chances it has to leak and the longer a leaked copy keeps working. So secrets are rotated: replaced with fresh values on a schedule, so that even a quietly stolen credential only works for a short time, and immediately after any suspected leak, because an exposed secret that is still valid is an open door. Rotation is why deleting a committed secret is never enough on its own — the leaked value has to be revoked and replaced, not merely hidden.
Better still is to not rely on a human remembering to rotate. Short-lived, dynamically issued credentials expire on their own after a set lifetime, and the system reissues fresh ones as they are needed. like a day pass that expires on its own, rather than a permanent badge that stays a liability the moment it goes missing A stolen short-lived credential is a much smaller problem, because it stops working shortly after it is taken whether or not anyone noticed the theft.
The through-line is the exposure window: the stretch of time a leaked secret is usable. A long-lived secret leaves that window open indefinitely. Rotation closes it on a schedule; short-lived credentials keep it narrow by default. Either way, the goal is to make sure that when a secret does leak — and eventually one will — it is already close to worthless.
Injecting, not baking in
Automated pipelines and containers need secrets too — a deploy step needs a token, a running container needs a database password — and they are exactly the places where a careless shortcut gets baked into something permanent. The safe pattern is the same one the code follows: supply the secret at runtime, and never store it in anything that gets committed or built.
For a pipeline, that means the secret is injected into the step as an environment value when the step runs, drawn from a secret store the pipeline is authorized to read, rather than written into the pipeline's configuration file that lives in the repository. For a container, it means the secret is provided when the container runs — not written into the image's build instructions. A value placed in a Containerfile with an ENV or ARG line, or otherwise built into the image, becomes part of a container image layer that ships wherever the image ships, and it leaks with the image as easily as a committed secret leaks with the repo.
The distinction is baking versus injecting. A baked-in secret is fixed into an artifact that gets stored, copied, and distributed, carrying the credential along for the ride. An injected secret is handed to the process at the moment it runs and lives only for as long as the process does. Let whatever orchestrates the run supply the real value at run time, and the artifact you build and store stays safe to move around.
A leaked key is an incident
Consider an automated agent pipeline: a system that holds API keys and uses them on its own to call external services, spend money, and reach real systems without a human in the loop for each action. Its secrets are not passwords guarding a login screen — they are live authority to act. A leaked key here does not merely embarrass; it lets someone else run up charges, read data, and take actions in your name, at machine speed.
That is what makes every habit in this concept load-bearing rather than fussy. The key stays out of the repository and out of the code, so it cannot leak through history that never forgets. It lives in a secret manager that encrypts it, gates each read, and logs who took it. It is scoped so the pipeline holds only the keys its own job needs, keeping the blast radius small if one is stolen. And it is rotated, so an exposed key is already on its way to worthless.
Skip these and a single mistake — one committed file, one over-broad grant, one credential that never expired — becomes a breach. Follow them and the same mistake stays a mistake: contained, revocable, and survivable. That gap is the entire reason secrets get handling nothing else in your configuration does.