How NOT to store secrets in Docker Containers: 3 mistakes to avoid
Think your secrets are safe in Docker? Think again! Storing sensitive data the wrong way can expose passwords, API keys, and certificates to attackers. In this post, we’ll explore three risky methods and a better alternative to keep your secrets secure in containerized environments.

Once again, we meet here to learn something about cybersecurity! This time, I’m re-sharing my very first technical article—the one that sparked my writing ambitions. It was my first step toward what some might call writer’s delusions, but my hope remains the same: to contribute something valuable to the internet and to the amazing community that inspired me to switch careers.
So, tell me… can your secrets be compromised in a containerized deployment? Are they truly safe from prying eyes? Let’s find out together.
What do I mean by “secrets”?
Passwords, API tokens, certificates—you name it. Any sensitive information your application needs to function falls into this category. For example, your web server might require a database password to access and manipulate data.
The goal? Ensuring that we pass this sensitive information to the application without exposing it to unauthorized users.
Sorry, folks—this is private!
Side note: I personally prefer encrypting my secrets. Why? Because if an attacker gains access to your cluster, machine, or even a running container, having secrets stored in plaintext means the job is already done for them. However, if they’re encrypted, the attacker still has an extra hurdle to overcome. It’s a simple but crucial security measure.
Now, let’s talk about how secrets actually get into a running container—and the worst ways to handle them.
1. Hardcoding secrets in source code
This is, without a doubt, one of the worst ways to handle secrets.
Some of you might be thinking, "Of course, that’s a terrible idea!"—while others might nervously glance at their own repositories and say, "Uh… yeah, I’d never do that… Nope, definitely not me."
Let’s be clear: if you store secrets in your source code, anyone with access to that code can see them.
- Open-source project? That’s an exceptionally bad idea.
- Closed-source project? You probably don’t want all your developers to have access to every secret.
Another major downside is that every time you need to update a secret, you have to modify and redeploy your code. This is inefficient and risky.
Imagine being the security engineer who wakes up a developer at 3 AM just to deploy a new secret. Not exactly the way to make friends, right?
So, let’s agree: don’t hardcode secrets in your source code.
2. Using environment variables
A slightly better approach, but still risky, is to pass secrets using environment variables.
Here’s a quick example of how to do this:
docker run -it --rm --name ubuntu -e MYSECRET=admin123 ubuntu /bin/bash

Now, let’s verify that the secret exists in the container’s environment:
env | grep MYSECRET

Sure enough, the value appears.
The Problem?
Anyone who can exec into that container can see the secret. And it gets worse:
Secrets can leak through logs.
Many applications log their environment variables when errors occur. If your secrets are stored there, they could easily end up in log files, accessible to anyone with log access.
Secrets can be found in process memory.
Let’s put the container to sleep and find its process ID:
sleep 100 # Inside the container

pidof sleep # Outside the container

Now, using sudo
, we can inspect the process environment:
sudo cat /proc/<pid>/environ | tr "\0" "\n"

And there it is—your supposedly secret value, fully exposed.
Secrets are accessible via docker inspect
.
docker inspect ubuntu -f ""

Anyone who can run this command—possibly even remotely—can see all environment variables in plain text.
For these reasons, environment variables are not the best way to store secrets.
3. Mounting a volume
A better alternative is using a temporary file system (tmpfs) to mount a volume for secrets.
But wait! I hear you say, "Aren’t we supposed to avoid writing secrets to disk?"
Yes, you’re right! But a temporary file system (tmpfs) exists only in memory—it looks like a file system, but nothing gets written to persistent storage.
Why would this be a better approach?
✅ No docker inspect
leaks: While docker inspect
can show that a volume is mounted, it won’t expose the secrets inside.
❌ Still accessible inside the container: Anyone who can exec into the container can still read the secret.
❌ Risk from /proc
remains: Secrets can still be accessed via the /proc
directory if an attacker has host access.
✅ No logging risks: Unlike environment variables, mounted secrets are far less likely to be logged.
Conclusion
Using a mounted volume to store secrets is a more secure approach than using environment variables, but it’s still not perfect.
When handling secrets, remember that security is about risk reduction, not perfection. Every method has its trade-offs, and your goal should always be to minimize exposure while keeping your system functional.
Happy Hacking!