Guía completa de Docker Secrets: mejores prácticas para gestionar contraseñas y claves API en contenedores

El teléfono vibra sin parar: 24 SMS, todos alertas de anomalías en la base de datos. Me conecto por SSH al servidor y las conexiones a la base de datos están al límite. Alguien está haciendo un escaneo por fuerza bruta.
Reviso los logs y descubro que el atacante usó directamente la contraseña correcta: la de MySQL que escribí hace tres meses en docker-compose.yml. El mes pasado un becario hizo fork del proyecto a su repositorio público para practicar, y así se filtró la contraseña.
Aunque no haya filtración de código, basta con que alguien ejecute docker inspect para ver todas las variables de entorno en texto plano. En este artículo hablamos de cómo gestionar contraseñas en contenedores, qué prácticas peligrosas evitar y qué herramientas protegen de verdad la información sensible.
¿Por qué las variables de entorno no son seguras?
Tres malas prácticas frecuentes
Empiezo contando errores que yo mismo cometí. ¿Te reconoces en alguno?
Primera: escribirlas directamente en el Dockerfile
FROM node:18
ENV DATABASE_PASSWORD=MyS3cr3tP@ssw0rd
ENV API_KEY=sk-1234567890abcdef
Es la peor de todas. Cada instrucción del Dockerfile crea una capa de imagen. Aunque luego uses ENV DATABASE_PASSWORD="" para sobrescribir, la contraseña sigue oculta en las capas históricas. Cualquiera con tu imagen puede sacarla con docker history <nombre-imagen>.
¿No lo crees? Yo tampoco, hasta que un compañero me lo demostró. Bajó una imagen interna de la empresa y con unas pocas órdenes extrajo el Access Key de AWS. Era una clave que dejó hace tres años un compañero que se fue, y nunca la borramos.
Segunda: escribirlas en docker-compose.yml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: SuperSecretPassword123
MYSQL_DATABASE: myapp
La usé mucho tiempo. La razón es simple: comodidad. Un docker-compose up -d y listo, desarrollo y depuración muy ágiles.
¿Cuál es el problema? Ese archivo hay que subirlo a Git. Aunque el repositorio sea privado, basta con que alguien gestione mal los permisos — o que alguien haga fork a un repositorio público, como me pasó — y la contraseña queda expuesta.
Tercera: usar archivo .env pero incluirlo en el control de versiones
# archivo .env
DB_PASSWORD=password123
API_SECRET=abcdef123456
Parece más listo: sacas la información sensible a un .env aparte y en docker-compose referencias con ${DB_PASSWORD}.
Pero mucha gente (yo incluido) también sube el .env a Git pensando que «el resto del equipo también tiene que poder ejecutarlo». El resultado es el mismo que escribirlo en docker-compose.
Lo correcto es añadir .env a .gitignore y dar una plantilla .env.example. Pero al principio, ¿quién piensa en todo eso?
Tres riesgos de seguridad de las variables de entorno
Aunque añadas .env a .gitignore y el código no se filtre, las variables de entorno siguen sin ser seguras. ¿Por qué?
Riesgo 1: docker inspect lo muestra todo
Prueba este comando:
docker inspect <ID-contenedor> | grep -A 20 "Env"
Todas las variables de entorno, incluidas las contraseñas, aparecen en texto plano.
¿Qué implica? Cualquiera con acceso al daemon de Docker — operaciones, DevOps, desarrolladores con permisos en el servidor — puede ver tus contraseñas. Sin cracking ni técnicas avanzadas: una sola orden.
En una startup donde trabajé, todos teníamos SSH al servidor de producción por comodidad. Un día un desarrollador frontend curioso por «qué hay en el contenedor» ejecutó docker inspect y la contraseña de la base de datos quedó en pantalla. Por suerte era alguien de confianza. ¿Y si no?
Riesgo 2: los procesos dentro del contenedor también pueden leerlas
Las variables de entorno no solo las ve Docker: todos los procesos del contenedor también. En Linux se guardan en /proc/<PID>/environ.
Si alguna dependencia de tu aplicación tiene una vulnerabilidad y alguien inyecta código, puede leer las variables de entorno y obtener la contraseña.
El año pasado una librería de Node.js tuvo un backdoor que leía credenciales AWS en variables de entorno y las enviaba a internet. Proyectos afectados: decenas de miles.
Riesgo 3: pueden filtrarse en los logs
Este es más sutil.
Muchas aplicaciones imprimen la configuración al arrancar, o en errores vuelcan variables de entorno a los logs. Los archivos de log suelen tener permisos flojos e incluso van a sistemas centralizados (ELK, por ejemplo).
Conocí un equipo que centralizaba todos los logs de contenedores en un clúster Elasticsearch. En una auditoría de seguridad encontraron cientos de contraseñas de base de datos en los logs… Los desarrolladores acostumbrados a console.log(process.env) en depuración y luego se olvidaban de borrarlo.
Peor aún: una vez que la contraseña entra en los logs, es difícil borrarla por completo — copias de seguridad, archivos, réplicas en muchos sitios.
Casos reales
En internet hay muchos incidentes de este tipo; muchas empresas no los hacen públicos. Te cuento algunos que conozco:
Caso 1: filtración en repositorios públicos de GitHub
En 2023 un análisis mostró que más de un millón de repositorios públicos en GitHub contenían claves API, contraseñas de base de datos y otra información sensible. No es que los desarrolladores sean tontos: a menudo es un descuido — configuración de prueba que no se borró, o un .env subido por error.
Un amigo que hace machine learning: su equipo publicó código de entrenamiento en GitHub y subió también credenciales AWS. En 24 horas alguien usó esas credenciales para levantar más de diez instancias GPU y minar. La factura superó los veinte mil dólares.
Caso 2: historial de pipelines CI/CD
Las herramientas CI/CD (Jenkins, GitLab CI, etc.) suelen conservar logs de variables de entorno en el historial de builds. Sin buena ofuscación, esos logs son un almacén de contraseñas.
En un proyecto con GitLab CI para despliegue automático, un compañero que se fue dejó permisos a un equipo externo. Alguien revisó logs de builds antiguos y obtuvo credenciales de la base de datos de producción. Por suerte querían ayudar y nos avisaron. ¿Y si no?
Caso 3: escaneo de imágenes en Docker Hub
Un informe de seguridad de Alibaba escaneó imágenes Docker públicas y encontró que el 76 % tenía vulnerabilidades, muchas con credenciales hardcodeadas.
Algunas empresas suben imágenes internas a Docker Hub público pensando que «nadie va a revisar la imagen de nuestra pequeña empresa». Pero hay crawlers y escáneres automatizados dedicados a eso. Tu imagen puede escanearse en minutos.
No digo esto para asustar, sino para que veas que estos problemas ocurren de verdad y son más comunes de lo que imaginas. Lo bueno: hay formas de evitarlos.
¿Qué es Docker Secrets y cómo usarlo?
Cómo funciona Docker Secrets
Bien, vamos a la solución.
Docker Secrets es el mecanismo oficial de Docker para gestionar claves. En resumen: la clave deja de ser una cadena en texto plano y pasa a ser un archivo cifrado.
El flujo es así:
- Creas un secret; Docker lo almacena cifrado en el log Raft del clúster
- Cuando el contenedor lo necesita, Docker lo transmite por TLS cifrado
- El secret se monta como archivo en
/run/secrets/dentro del contenedor - Ese directorio es
tmpfs— sistema de archivos solo en memoria, sin escribir en disco - Al detener el contenedor, el secret se borra de la memoria
Lo clave: el secret no aparece en variables de entorno, no sale en docker inspect, no se empaqueta en la imagen con docker commit.
¿Suena abstracto? La primera vez que leí la documentación de Docker también me perdí. Prueba en la práctica y se entiende.
Una limitación: Docker Secrets nativo solo funciona en modo Swarm. Al principio me frustró — desarrollo local en una sola máquina, ¿por qué no puedo usarlo?
Luego descubrí que docker-compose también soporta file secrets (menos potente, pero usable). En producción con clúster, Swarm o Kubernetes van bien.
Tutorial paso a paso: MySQL + WordPress
Un ejemplo completo. Supongamos que desplegamos un blog WordPress y hay que proteger la contraseña root de MySQL y la del usuario.
Paso 1: crear archivos de secret
Escribe las contraseñas en archivos locales (no los subas a Git):
echo "MyRootPassword123" > db_root_password.txt
echo "MyUserPassword456" > db_password.txt
Paso 2: inicializar Swarm (si aún no lo hiciste)
docker swarm init
Sí, solo esa orden. Si Swarm no está activo, ejecútala. Puedes usar Swarm en una sola máquina, no hace falta un clúster de varios nodos.
Paso 3: crear Docker secrets
docker secret create mysql_root_password db_root_password.txt
docker secret create mysql_password db_password.txt
Después de crearlos, borra de inmediato los archivos locales con contraseñas:
rm db_root_password.txt db_password.txt
Importante. Una vez que Docker leyó y almacenó cifrado, el texto plano local debe desaparecer.
Comprueba que los secrets existen:
docker secret ls
Verás algo así:
ID NAME CREATED UPDATED
abc123... mysql_root_password 5 seconds ago 5 seconds ago
def456... mysql_password 3 seconds ago 3 seconds ago
Nota: no ves el contenido del secret, solo nombre y metadatos. Ese es el punto: una vez creado, ni el administrador obtiene el texto plano.
Paso 4: crear docker-compose.yml
version: '3.8'
secrets:
mysql_root_password:
external: true
mysql_password:
external: true
services:
db:
image: mysql:8.0
secrets:
- mysql_root_password
- mysql_password
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
MYSQL_USER: wordpress
MYSQL_DATABASE: wordpress
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:latest
depends_on:
- db
ports:
- "8080:80"
secrets:
- mysql_password
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/mysql_password
WORDPRESS_DB_NAME: wordpress
volumes:
db_data:
Puntos clave:
secretsa nivel raíz: declara qué secrets usas;external: trueindica que ya existen y no los crea composeservices.db.secrets: qué secrets usa este servicioMYSQL_ROOT_PASSWORD_FILE: la imagen oficial de MySQL soporta variables con sufijo_FILEy lee la contraseña del archivo
Ese sufijo _FILE es importante: no todas las imágenes lo soportan. Si no, lee el archivo en tu script de arranque:
# ejemplo entrypoint.sh
export DB_PASSWORD=$(cat /run/secrets/db_password)
# luego arranca tu aplicación
Paso 5: desplegar
docker stack deploy -c docker-compose.yml myapp
Aquí se usa docker stack deploy, no docker-compose up. En modo Swarm hay que usar stack.
Paso 6: verificar
Entra al contenedor MySQL y comprueba el montaje:
docker exec -it <ID-contenedor> sh
ls -la /run/secrets/
Verás:
total 8
-r--r--r-- 1 root root 18 Dec 18 10:00 mysql_password
-r--r--r-- 1 root root 18 Dec 18 10:00 mysql_root_password
Prueba a leer:
cat /run/secrets/mysql_root_password
Aparece la contraseña. Pero lo importante: solo existe en memoria, no se escribe en disco ni aparece en variables de entorno.
Comprueba que docker inspect no muestra la contraseña:
docker inspect <ID-contenedor> | grep -i password
Solo verás rutas como MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password, no la contraseña en sí.
Esa es la ventaja de Docker Secrets: la aplicación dentro del contenedor puede leerla, pero desde fuera no hay texto plano.
Limitaciones de Docker Secrets y alternativas
Docker Secrets no es perfecto. Principales limitaciones:
Limitación 1: requiere modo Swarm
Lo que más molesta. Para desarrollo local en una sola máquina necesitas docker swarm init antes de usar secrets. Aunque Swarm en un solo nodo funciona, parece matar moscas a cañonazos.
Y una vez en Swarm, hay que usar docker stack deploy en lugar del familiar docker-compose up. Cambian los hábitos.
Limitación 2: el secret no se puede modificar
Una vez creado, el contenido es de solo lectura. Para cambiar la contraseña hay que borrar y recrear:
docker secret rm mysql_password
echo "NewPassword789" | docker secret create mysql_password -
Y actualizar todos los servicios que lo usan:
docker service update --secret-rm mysql_password --secret-add mysql_password myapp_db
Más engorroso que cambiar una variable de entorno y reiniciar el contenedor.
Limitación 3: no sirve para contenedores sueltos
Con docker run directo no puedes usar Docker Secrets. Tiene que ser servicio creado con docker service create o desplegado con docker stack.
Poco práctico para pruebas rápidas. A veces solo quiero levantar un contenedor de base de datos y me toca todo el flujo Swarm.
Alternativa para desarrollo local: docker-compose file secrets
docker-compose tiene un atajo: secrets tipo file. No requiere Swarm; monta un archivo local como secret.
Configuración aproximada:
version: '3.8'
secrets:
db_password:
file: ./secrets/db_password.txt
services:
db:
image: mysql:8.0
secrets:
- db_password
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
Crea el archivo local:
mkdir secrets
echo "MyPassword123" > secrets/db_password.txt
Importante: añade secrets/ a .gitignore.
Más seguro que variables de entorno: la contraseña no sale en docker inspect ni se empaqueta en la imagen. Pero no iguala a Docker Secrets real porque:
- El archivo sigue en texto plano en disco local
- No hay transmisión cifrada
- No hay gestión centralizada
Para desarrollo local suele bastar. En producción, Swarm o Kubernetes de verdad.
Trucos avanzados
Con lo básico dominado, algunos consejos útiles.
Truco 1: ruta de montaje personalizada
Por defecto el secret va en /run/secrets/<secret_name>, pero puedes personalizar ruta y nombre:
services:
myapp:
image: myapp:latest
secrets:
- source: db_password
target: /app/config/database.pwd
mode: 0400
mode: 0400 fija permisos (solo lectura del owner), más seguro.
Truco 2: varios entornos
Desarrollo, pruebas y producción con contraseñas distintas pero el mismo nombre de secret:
# producción
docker secret create db_password prod_password.txt
# pruebas (otro clúster Swarm)
docker secret create db_password test_password.txt
El código solo lee /run/secrets/db_password, sin importar el valor. Mismo código, distintos clústeres por entorno.
Truco 3: rotación de secrets
Cambiar contraseñas periódicamente es buena práctica. La rotación en Docker es un poco enrevesada pero manejable:
# 1. Crear nueva contraseña
echo "NewPassword" | docker secret create db_password_v2 -
# 2. Actualizar servicio: añadir nuevo secret, quitar el viejo
docker service update \
--secret-rm db_password \
--secret-add source=db_password_v2,target=/run/secrets/db_password \
myapp_db
# 3. Tras confirmar que el servicio va bien, borrar el secret viejo
docker secret rm db_password
El truco: el nuevo secret se llama db_password_v2, pero con target la ruta de montaje sigue siendo /run/secrets/db_password. La aplicación no cambia.
Truco 4: crear secret desde stdin
¿No quieres rastro en disco? Crea desde entrada estándar:
echo "MySecretPassword" | docker secret create db_password -
El - final indica lectura desde stdin. Ni archivo temporal.
O más seguro, entrada manual (no queda en el historial del shell):
docker secret create db_password -
# pega la contraseña y Ctrl+D para terminar
Comparación con otras herramientas de gestión de claves
Matriz de cuatro herramientas
Docker Secrets no es la única opción. Según escala e infraestructura, puedes necesitar otras. Tabla comparativa:
| Herramienta | Escenario | Ventaja principal | Principal inconveniente | Dificultad |
|---|---|---|---|---|
| Docker Secrets | Clúster Docker Swarm | Soporte nativo, costo cero, configuración simple | Solo Swarm, funciones básicas, no multiplataforma | ⭐ Baja |
| Kubernetes Secrets | Producción K8s | Nativo del ecosistema K8s, ligado al ciclo de vida del Pod | etcd sin cifrado por defecto, funciones básicas, requiere configuración extra | ⭐⭐ Media |
| HashiCorp Vault | Empresa, multinube, cumplimiento estricto | Máxima potencia, claves dinámicas, permisos granulares, auditoría, varios backends | Arquitectura compleja, despliegue propio, curva de aprendizaje alta | ⭐⭐⭐ Alta |
| AWS Secrets Manager | Entorno 100 % AWS, RDS/Lambda | Integración profunda con AWS, rotación automática RDS, servicio gestionado | Atado a AWS, difícil multinube, costo por uso | ⭐⭐ Media |
En resumen:
- Proyecto pequeño, Docker Swarm → Docker Secrets basta, gratis y simple
- Producción Kubernetes → K8s Secrets + External Secrets Operator hacia Vault externo
- Gran empresa, multinube, alta seguridad → Vault, inversión inicial, beneficio a largo plazo
- Todo en AWS → Secrets Manager, muy cómodo con RDS y Lambda
No hay mejor herramienta, solo la más adecuada. Mi camino: al principio proyectos pequeños con docker-compose file secrets; al crecer el equipo, Docker Swarm + Docker Secrets; ahora la empresa en K8s evaluando Vault.
Cada migración tiene costo, pero la seguridad mejora paso a paso.
Recomendaciones de elección
¿Cómo elegir? Algunas referencias:
Si eres…
Desarrollador individual / proyecto pequeño:
- Desarrollo local: docker-compose file secrets basta
- VPS con varios servicios: Docker Swarm + Docker Secrets
- Presupuesto ajustado: no metas Vault, es demasiado
Startup (10-50 personas):
- Con Docker: Docker Secrets
- Con K8s: K8s Secrets + External Secrets Operator (evolución gradual hacia Vault)
- Todo en AWS: Secrets Manager directo
Gran empresa:
- Multinube: Vault casi obligatorio
- Auditoría y cumplimiento: Vault
- Equipo de seguridad dedicado: Vault
- Presupuesto amplio: Vault
Árbol de decisión simplificado:
¿Usas K8s?
└─ Sí → K8s Secrets; funciones avanzadas → Vault
└─ No → ¿Docker Swarm?
└─ Sí → Docker Secrets
└─ No → ¿En AWS?
└─ Sí → AWS Secrets Manager
└─ No → docker-compose file secrets (dev) / Vault (prod)
Casos reales
Dos caminos que conozco:
Caso 1: evolución de una startup
Un amigo con SaaS: al principio 5 personas, todo en Docker en un VPS. docker-compose + file secrets, archivos de contraseña en el servidor, gestión manual.
Seis meses después, ronda ángel, 15 personas, microservicios. Pasaron a Docker Swarm y Docker Secrets: gestión centralizada en el clúster, sin copiar archivos en cada servidor.
Un año más, ronda A y migración a K8s (contrataron DevOps): K8s Secrets. AWS RDS y S3 con Secrets Manager; External Secrets Operator sincroniza a K8s.
Ahora evalúan Vault unificado por estrategia multinube (backup en GCP); Secrets Manager atado a AWS les limita.
La arquitectura de seguridad evoluciona con el negocio; no hace falta la solución más compleja desde el día uno.
Caso 2: empresa tradicional de un salto
Otro amigo en un banco, containerización el año pasado. Sector financiero, cumplimiento estricto: Vault desde el inicio.
Curva de aprendizaje alta, pero:
- Auditoría completa de cada acceso a claves
- Claves dinámicas (credenciales temporales de BD que expiran solas)
- Integración con LDAP existente
Dos personas-mes en despliegue y formación; problema resuelto de una vez, mantenimiento bajo después.
Contextos distintos, elecciones distintas. Vault puede no compensar en una pyme; Docker Secrets puede quedarse corto en una gran empresa.
Checklist de mejores prácticas para producción
Medidas de seguridad inmediatas
Uses o no Docker Secrets, haz esto ya:
✅ Revisar historial de Git en busca de información sensible
# buscar posibles contraseñas
git log -p -S 'password' -S 'secret' -S 'api_key'
# comprobar si .env se subió alguna vez
git log --all --full-history -- .env
Si encuentras algo, limpia el historial con git filter-repo o BFG Repo-Cleaner. Luego cambia de inmediato todas las contraseñas filtradas.
✅ Configurar .gitignore
# .gitignore
.env
*.env
secrets/
db_password.txt
*_password.txt
*_secret.txt
*.pem
*.key
No confíes en la suerte; añádelo.
✅ Activar GitHub Secret Scanning
En GitHub: Settings → Security → Secret scanning alerts. Si subes una clave por error, GitHub avisa.
La versión gratuita cubre repos públicos; privados requieren Pro/Team/Enterprise. Los públicos son los que más lo necesitan.
✅ Rotar credenciales que puedan haberse filtrado
Inventario:
- Contraseñas de base de datos
- Claves API (AWS/OpenAI/Stripe, etc.)
- JWT secret
- OAuth client secrets
- Claves privadas SSL
Cambia todo lo que puedas. Tras una filtración, aún hay tiempo de reaccionar.
Pasos para implementar Docker Secrets
¿Vas con Docker Secrets? Este orden:
Paso 1: inventario de información sensible
Lista de qué es sensible:
✓ Contraseña root MySQL
✓ Contraseña de aplicación en BD
✓ Contraseña Redis
✓ Clave de firma JWT
✓ Claves API de terceros (pago/SMS, etc.)
✓ Clave privada de certificado SSL
✓ OAuth client secret
Paso 2: elegir herramienta
Según la comparación: Docker Secrets, K8s Secrets o Vault. Elige una; puedes migrar después.
Paso 3: crear secrets
# Docker Swarm
docker swarm init
docker secret create db_password <(echo "contraseña")
docker secret create api_key <(echo "clave")
# docker-compose file secrets
mkdir secrets
echo "contraseña" > secrets/db_password.txt
echo "clave" > secrets/api_key.txt
chmod 600 secrets/*
Paso 4: modificar configuración
Cambia environment por secrets en docker-compose.yml:
# antes
services:
app:
environment:
DB_PASSWORD: "hardcoded_password" # ❌
# después
services:
app:
secrets:
- db_password
environment:
DB_PASSWORD_FILE: /run/secrets/db_password # ✅
Paso 5: modificar código de aplicación (si hace falta)
Si tu framework no soporta sufijo _FILE, lee el archivo en código:
// ejemplo Node.js
const fs = require('fs');
const dbPassword = process.env.DB_PASSWORD_FILE
? fs.readFileSync(process.env.DB_PASSWORD_FILE, 'utf8').trim()
: process.env.DB_PASSWORD;
Paso 6: probar
En entorno de pruebas, confirma:
- El servicio arranca
- Lee la contraseña correctamente
docker inspectno muestra texto plano- La funcionalidad es correcta
Paso 7: despliegue a producción
Si todo va bien, despliega. Como seguro, conserva temporalmente variables de entorno antiguas como respaldo; bórralas cuando el nuevo esquema sea estable.
Mantenimiento continuo de seguridad
Docker Secrets no es «configurar y olvidar»:
Rotar claves periódicamente (cada 90 días recomendado)
Calendario cada trimestre. Tras una baja, cambia las contraseñas relacionadas.
Monitorizar logs de acceso a claves
Con Vault, revisa audit logs y patrones anómalos.
Docker Secrets y K8s Secrets no tienen auditoría por defecto; para eso hace falta otras herramientas (Falco, por ejemplo).
Principio de mínimo privilegio
No des a todos los servicios acceso a todos los secrets:
services:
frontend:
secrets:
- api_key # el frontend solo necesita API key
backend:
secrets:
- db_password # el backend necesita BD
- api_key
Herramientas de escaneo de claves
Integra en CI/CD:
- trufflehog: historial Git
- git-secrets: evitar commits sensibles
- gitleaks: detectar claves en código
Un pre-commit hook evita muchos envíos accidentales.
Cinco trampas frecuentes
1. No uses ARG para información sensible
# ❌ incorrecto
ARG DB_PASSWORD=secret
ENV DATABASE_URL=postgres://user:${DB_PASSWORD}@db/myapp
ARG queda en el historial de build; docker history lo muestra.
2. No imprimas secrets en stdout
# ❌ incorrecto
password = open('/run/secrets/db_password').read()
print(f"Using password: {password}") # ¡va a docker logs!
Los sistemas de logs capturan stdout. Una vez en logs, difícil de borrar.
3. No reutilices el mismo secret entre entornos
Separa contraseñas de desarrollo, pruebas y producción. Si filtra pruebas, no arrastre producción.
4. No copies el secret a otra ruta dentro del contenedor
# ❌ incorrecto
cp /run/secrets/db_password /app/config/password.txt
/run/secrets es tmpfs en memoria; al parar el contenedor desaparece. Copiar a otra ruta puede escribir en disco y empeorar la seguridad.
Lee directamente desde /run/secrets.
5. No olvides expiración y rotación
Con claves dinámicas con expiración (Vault), configura renovación o rotación automática. No esperes a que expire a las 3 de la madrugada y caigan todos los servicios.
Conclusión
En pocas palabras: no pongas contraseñas donde puedan verse.
Las variables de entorno parecen cómodas, pero docker inspect las expone. ENV en Dockerfile es peor: quedan para siempre en las capas. Muchos incidentes vienen de estos atajos «por comodidad».
Docker Secrets no es perfecto — requiere Swarm, no se modifica, incómodo en desarrollo local. Pero resuelve lo esencial: almacenamiento cifrado, transmisión cifrada, fuera de variables de entorno e imágenes. En clúster Swarm es la opción más directa.
Con Kubernetes: K8s Secrets; necesidades empresariales: Vault; todo en AWS: Secrets Manager. No hay mejor herramienta, solo la adecuada.
Lo importante es actuar. Hoy mismo comprueba:
- ¿Hay contraseñas en tu historial de Git?
- ¿
.envestá en.gitignore? - ¿
docker inspectmuestra tus contraseñas?
Si respondes sí / no / sí, toca dedicar tiempo a corregirlo. No hace falta Vault de golpe; pasar de variables de entorno a file secrets ya mejora la situación.
La seguridad es un proceso continuo, no una configuración única. Rotar contraseñas, vigilar accesos anómalos, escanear código: estos hábitos importan más que la herramienta.
Compártelo con tu equipo. La gestión de claves no es de una sola persona; es responsabilidad colectiva. Estándares comunes y revisiones periódicas reducen el riesgo de verdad.
Que no te despierten más alertas a las 3 de la madrugada.
Recursos de referencia:
- Documentación oficial de Docker - Manage sensitive data with Docker secrets
- Documentación oficial de HashiCorp Vault
- AWS Secrets Manager
- trufflehog - herramienta de escaneo de claves en Git
Flujo completo de gestión segura con Docker Secrets
Gestiona contraseñas de contenedores y claves API de forma segura, compara Docker/K8s/Vault y ofrece un checklist completo para producción
⏱️ Estimated time: 1 hr
- 1
Step 1: Entender el problema de seguridad y las malas prácticas
Problema de seguridad:
• El atacante usó directamente la contraseña correcta: la de MySQL escrita en docker-compose.yml
• El mes pasado un becario hizo fork del proyecto a su repositorio público para practicar
• La filtración de contraseñas provocó un escaneo por fuerza bruta de la base de datos
Malas prácticas:
• Escribir contraseñas directamente en archivos de configuración
• Creer que las variables de entorno son seguras
• Incluso decir 'mi repositorio es privado, no pasa nada'
• Todo esto son riesgos de seguridad
Errores frecuentes:
• Contraseña en el Dockerfile
• Contraseña en docker-compose.yml
• Contraseña en variables de entorno pero subida a Git
• Usar archivo .env sin añadirlo a .gitignore - 2
Step 2: Gestionar claves con Docker Secrets
Solución con Docker Secrets:
• Crear secretos con docker secret create: docker secret create mysql_password -
• Configurar secrets en docker-compose.yml: secrets: - mysql_password
• Acceder dentro del contenedor por /run/secrets/: cat /run/secrets/mysql_password
• Los secretos se almacenan cifrados en Docker Swarm
Crear secretos:
• echo "your-password" | docker secret create mysql_password -
• Referenciar en docker-compose.yml: secrets: - mysql_password
• Leer dentro del contenedor: cat /run/secrets/mysql_password - 3
Step 3: Comparación de otras soluciones y mejores prácticas
Comparación de otras soluciones:
• Docker Secrets (adecuado para Docker Swarm, almacenamiento cifrado)
• Kubernetes Secrets (entorno K8s, codificación Base64)
• HashiCorp Vault (gestión empresarial, claves dinámicas)
• AWS Secrets Manager (entorno AWS, integración con servicios AWS)
• Archivo de variables de entorno (.env, no recomendado en producción)
Mejores prácticas:
• En producción, usar siempre Secrets para información sensible
• Rotar claves periódicamente
• Usar herramientas de escaneo (trufflehog) para revisar repositorios Git
• Configurar control de acceso para evitar filtraciones
• No codificar contraseñas en el código
FAQ
¿Por qué no escribir contraseñas en el Dockerfile o docker-compose.yml?
Malas prácticas:
• Escribir contraseñas directamente en archivos de configuración
• Creer que las variables de entorno son seguras
• Incluso decir 'mi repositorio es privado, no pasa nada'
• Todo esto son riesgos de seguridad
Errores frecuentes:
• Contraseña en el Dockerfile
• Contraseña en docker-compose.yml
• Contraseña en variables de entorno pero subida a Git
• Usar archivo .env sin añadirlo a .gitignore
¿Cómo gestionar claves con Docker Secrets?
• Crear secretos con docker secret create (docker secret create mysql_password -)
• Configurar secrets en docker-compose.yml (secrets: - mysql_password)
• Acceder dentro del contenedor por /run/secrets/ (cat /run/secrets/mysql_password)
• Los secretos se almacenan cifrados en Docker Swarm
Crear secretos:
• echo "your-password" | docker secret create mysql_password -
• Referenciar en docker-compose.yml: secrets: - mysql_password
• Leer dentro del contenedor: cat /run/secrets/mysql_password
¿En qué se diferencia Docker Secrets de otras soluciones de gestión de claves?
• Docker Secrets (adecuado para Docker Swarm, almacenamiento cifrado)
• Kubernetes Secrets (entorno K8s, codificación Base64)
• HashiCorp Vault (gestión empresarial, claves dinámicas)
• AWS Secrets Manager (entorno AWS, integración con servicios AWS)
• Archivo de variables de entorno (.env, no recomendado en producción)
Recomendaciones:
• Entorno Docker Swarm: Docker Secrets
• Entorno K8s: Kubernetes Secrets
• Necesidades empresariales: Vault
• Entorno AWS: Secrets Manager
¿Cuáles son las mejores prácticas de gestión de claves en producción?
• En producción, usar siempre Secrets para información sensible
• Rotar claves periódicamente
• Usar herramientas de escaneo (trufflehog) para revisar repositorios Git
• Configurar control de acceso para evitar filtraciones
• No codificar contraseñas en el código
Revisiones periódicas:
• Escanear repositorios Git con trufflehog
• Comprobar si hay filtraciones de claves
• Configurar CI/CD con escaneo automático
• Rotar claves de inmediato si se detecta una filtración
19 min de lectura · Publicado el: 18 dic 2025 · Actualizado el: 21 ago 2026
Guía práctica de Docker
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Seguridad en Docker: guía completa para evitar ejecutar contenedores como root
Los contenedores Docker ejecutados por defecto como root suponen un riesgo grave. Este artículo explica el escape de contenedores y ofrece soluciones completas: instrucción USER en Dockerfile, parámetro --user, control fino con Capabilities y configuración de AppArmor para construir contenedores seguros en producción.
Parte 30 de 38
Siguiente
Guía completa de límites de recursos en Docker: evita que una fuga de memoria tumbe el servidor
¿Cómo una fuga de memoria en un contenedor puede tumbar todo el servidor? Desde los cgroups hasta --memory y --cpus en la práctica, pasando por docker stats, cAdvisor y Prometheus, aprende a proteger tu entorno de producción con límites de recursos.
Parte 32 de 38



Comentarios
Inicia sesión con GitHub para dejar un comentario