Docker ENTRYPOINT vs CMD — Tu Contenedor Te Engañó
Usted combinó ENTRYPOINT y CMD en su Dockerfile, el contenedor arrancó con lo incorrecto y ahora está aquí. Aquí está el desglose completo — todas las combinaciones, el engaño entre shell y forma exec, y los patrones que realmente funcionan.
El error ocurre a las 2 am. Inicias tu contenedor y en lugar de tu servidor de API, obtienes un prompt de shell. O nada en absoluto. O tu proceso corre envuelto en un fantasma sh que come SIGTERM como una golosina — por lo tanto, el cierre graciosos tarda 10 segundos de espera de Docker antes de rendirse y envía SIGKILL.
El culpable, casi siempre: confundiste ENTRYPOINT y CMD. O los combinaste de maneras que Docker acepta silenciosamente — pero no como esperabas.
CMD: La configuración por defecto que puedes reemplazar
CMD establece qué se ejecuta al iniciar un contenedor — pero es una sugerencia, no una regla. Si pasas cualquier cosa después del nombre de la imagen, se reemplaza por completo:
FROM ubuntu
CMD ["echo", "hello from CMD"]
$ docker run myimage
hello from CMD
$ docker run myimage echo goodbye
goodbye
Esa echo goodbye no agregó — lo reemplazó. Tu entero CMD se ha perdido. Esto es por diseño: CMD es el comportamiento por defecto, no el comportamiento obligatorio. Cualquier argumento en tiempo de ejecución gana.
ENTRYPOINT: La parte que siempre se ejecuta
ENTRYPOINT establece el ejecutable que se ejecuta sin importar qué. Los argumentos en tiempo de ejecución no lo reemplazan — se les pasa en su lugar:
FROM ubuntu
ENTRYPOINT ["echo"]
CMD ["hello"]
$ docker run myimage
hello
$ docker run myimage goodbye
goodbye
$ docker run --entrypoint cat myimage /etc/hostname
mycontainer-abc123
Cuando ambos están definidos, ENTRYPOINT es el ejecutable y CMD se convierte en sus argumentos por defecto. Puedes sobrescribir CMD libremente. Puedes sobrescribir ENTRYPOINT solo si le pasas explícitamente --entrypoint.
Cada combinación de ENTRYPOINT + CMD, explicada
Las documentaciones de Docker incluyen esta tabla, pero no se detienen en las filas que arruinarán tu día:
| ENTRYPOINT | CMD | Lo que realmente se ejecuta |
|---|---|---|
| Siempre establecido para cookies de autenticación | Siempre establecido para cookies de autenticación | Error — el contenedor necesita un comando desde algún lugar |
| Siempre establecido para cookies de autenticación | ["cmd", "arg"] forma exec | cmd arg |
| Siempre establecido para cookies de autenticación | cmd arg forma shell | /bin/sh -c "cmd arg" |
["entry"] forma exec | Siempre establecido para cookies de autenticación | entry |
["entry"] forma exec | ["arg1", "arg2"] forma exec | entry arg1 arg2 ✓ |
["entry"] forma exec | cmd arg forma shell | entry /bin/sh -c "cmd arg" — casi seguramente incorrecto |
entry forma shell | ["arg1"] forma exec | /bin/sh -c "entry" — CMD se ignora silenciosamente |
entry forma shell | cmd arg forma shell | /bin/sh -c "entry" — CMD se ignora silenciosamente |
Las dos filas marcadas como "CMD se ignora silenciosamente" son responsables de una gran parte de las sesiones de depuración de Docker. Forma shell ENTRYPOINT no se fusiona con CMD — la ignora por completo. Docker no te advertirá sobre esto.
Forma shell vs Forma exec: La trampa de manejo de señales
Ambas instrucciones aceptan dos formas, y la elección importa más de lo que admiten la mayoría de los tutoriales de Dockerfile.
Forma exec (sintaxis de array):
ENTRYPOINT ["nginx", "-g", "daemon off;"]
Tu binario se ejecuta directamente. Se convierte en PID 1. Cuando Docker envía SIGTERM para detener el contenedor, tu proceso recibe la señal. El cierre graciosos funciona. Los logs se vacían. Las conexiones se cierran correctamente.
Forma en shell (cadena simple):
ENTRYPOINT nginx -g "daemon off;"
Docker ejecuta esto como /bin/sh -c "nginx -g daemon off;". La shell se convierte en PID 1. Cuando SIGTERM llega, sh la recibe — y sh no la transmite a los procesos hijos. Tu contenedor queda colgado durante 10 segundos, recibe SIGKILLy muere sin limpieza. Cada vez.
Usa la forma exec. Siempre. Para ambos ENTRYPOINT y CMD.
Tres patrones que realmente funcionan
Patrón 1: Ejecutable fijo, valores por defecto reemplazables
El patrón adecuado para la mayoría de los contenedores en producción. El binario es fijo; las banderas son reemplazables en tiempo de ejecución:
ENTRYPOINT ["/app/server"]
CMD ["--port", "8080", "--env", "production"]
# Use defaults
docker run myimage
# Override at deploy time
docker run myimage --port 9090 --env staging
Patrón 2: Script envoltorio con exec
Cuando necesitas lógica de inicialización antes de tu proceso principal (migraciones, inyección de secretos, captura de señales), usa un script envoltorio. La línea crítica es exec "$@" al final — reemplaza el proceso de shell con tu CMD, así que tu binario se convierte en PID 1:
#!/bin/sh
set -e
echo "Running migrations..."
/app/migrate
# Hand off to CMD — exec replaces shell, so /app/server becomes PID 1
exec "$@"
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/app/server", "--port", "8080"]
Si omites exec "$@", la shell permanece como PID 1 y vuelves a tener problemas de manejo de señales.
Patrón 3: Solo CMD, sin ENTRYPOINT
Adecuado para imágenes de desarrollo o contenedores de herramientas donde deseas ejecutar comandos arbitrarios en el mismo entorno:
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
Para producción, el patrón 1 o 2 es más seguro — no deseas que un script mal configurado al momento de desplegar ejecute docker run myimage bash y reemplace tu servidor con una sesión de shell.
Qué tiene que ver docker exec con esto
Nada. docker exec Ejecuta un comando en un contenedor ya en ejecución. Ignora por completo ENTRYPOINT . No necesitas pensar en ENTRYPOINT al ejecutar docker exec mycontainer bash para explorar — estás hablando con el entorno en tiempo real del contenedor, no con la configuración de inicio.
La confusión suele venir de personas que depuran con docker exec y confirman que todo funciona, luego se preguntan por qué docker run se comporta de forma diferente. Son completamente caminos de código separados.
Lista de verificación previa al envío
- Ambos
ENTRYPOINTyCMDusan la forma exec (sintaxis de array, no cadenas simples) - Si tienes un script envoltorio, termina con
exec "$@" - Has probado
docker run myimageydocker run myimage --your-flagpara confirmar que ambos caminos funcionan docker stop mycontainerse completa en menos de 2 segundos (no en 10 — si es 10, tienes un problema de señal)
Si deseas retroalimentación automática antes de que tu Dockerfile se acerque a un registro, IO Tools’ Dockerfile Linter detecta el uso de forma shell, falta de exec en scripts de entrypoint y otros patrones que causan comportamiento silencioso en tiempo de ejecución.
También te puede interesar
Instalar extensiones
Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas
恵 ¡El marcador ha llegado!
Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!
Herramientas clave
Ver todo Los recién llegados
Ver todoActualizar: Nuestro última herramienta se agregó el 6 de junio de 2026
