Cambiar tema

Guía de depuración de contenedores Docker: la forma correcta de usar exec

Easton editorial illustration: container packing dock

La API del entorno de pruebas empezó a devolver 502 de repente. Ejecutas docker ps y el contenedor aparece como “Up 2 hours”, aparentemente sano. Miras los logs y solo ves un frío “Connection refused”.

Es lo más frustrante: sabes que el contenedor corre, pero no qué pasa dentro. ¿La configuración es correcta? ¿El proceso arrancó? ¿Hay algo escuchando en el puerto? Para confirmarlo hay que “entrar” al contenedor, como abrir una caja negra.

Cuando empecé con Docker tampoco sabía cómo entrar. Unos decían docker exec, otros docker attach, otros “reinícialo”. Tras varios tropiezos entendí que la depuración de contenedores tiene una forma correcta de hacerse.

Este artículo te enseña a usar docker exec bien: diferencia con attach, qué hacer si faltan herramientas y trucos prácticos. Con esto, la próxima vez no tendrás que recurrir solo al reinicio.

docker exec básico: la forma correcta de entrar al contenedor

La forma más simple de entrar

Para entrar en un contenedor en ejecución, el comando más usado es:

docker exec -it my-nginx bash

Tres partes clave:

  • -it: abreviatura de dos flags; -i mantiene la entrada estándar abierta y -t asigna un terminal. En la práctica, te permite “hablar” con el contenedor
  • my-nginx: nombre del contenedor, o el ID (por ejemplo docker exec -it abc123def456 bash)
  • bash: comando a ejecutar dentro; aquí abre un terminal bash

Tras ejecutarlo, el prompt cambia a algo como root@abc123def456:/#. Ya estás dentro y puedes usar comandos como en un Linux normal.

¿Y si no hay bash?

A veces verás:

$ docker exec -it my-alpine bash
OCI runtime exec failed: exec failed: container_linux.go:380:
starting container process caused: exec: "bash": executable file not found

No pasa nada: muchas imágenes Alpine solo traen sh. Cambia el comando:

docker exec -it my-alpine sh

Mi regla: prueba bash primero; si falla, usa sh. Cubre casi todos los contenedores.

Usar el ID del contenedor

Si no recuerdas el nombre, basta con un prefijo del ID:

# Ver el ID
$ docker ps
CONTAINER ID   IMAGE    COMMAND                  CREATED
abc123def456   nginx    "/docker-entrypoint.…"   2 hours ago

# Entrar con prefijo (3-4 caracteres bastan)
$ docker exec -it abc1 bash

Docker resuelve el prefijo si no hay ambigüedad. Muy útil con muchos contenedores.

Salir del contenedor correctamente

Dentro del contenedor, escribe exit o pulsa Ctrl+D:

root@abc123def456:/# exit
exit
$

Lo importante: con exec, al salir el contenedor no se detiene. Eso explica por qué conviene este método.

exec vs attach: no los confundas

La diferencia esencial

Muchos tutoriales mencionan docker exec y docker attach sin aclarar la diferencia. Yo usé mal attach una vez y un contenedor se paró sin avisar.

En resumen:

  • docker exec: inicia un proceso nuevo dentro del contenedor, como abrir otra ventana
  • docker attach: se conecta al proceso principal (PID 1), como compartir la misma pantalla

Ejemplo: con docker attach my-nginx, si pulsas Ctrl+C o exit, el proceso principal (Nginx) recibe la señal y el contenedor se para. En producción, eso duele.

Con docker exec -it my-nginx bash ejecutas un bash independiente; al salir, Nginx sigue corriendo.

¿Cuándo usar cada uno?

Mi consejo: en el 99 % de los casos, usa exec.

attach solo tiene sentido si necesitas interactuar con el proceso principal, por ejemplo:

  • Un programa interactivo (REPL de Python)
  • Ver la salida del proceso principal en directo
  • Varios terminales viendo lo mismo (tipo pantalla compartida)

Pero son casos raros. Lo habitual es entrar, ver archivos, cambiar config o ejecutar comandos: exec es más seguro.

Tabla comparativa

Característicadocker execdocker attach
Crea proceso nuevo✓ Sí✗ No
Al salir, para el contenedor✗ No✓ Sí (¡peligro!)
Ejecutar cualquier comando✓ Sí✗ No
Terminales independientes✓ Sí✗ No (comparten tty)
Recomendado para depurar⭐⭐⭐⭐⭐

Por eso insisto en exec: attach nació para ver salida, no para depuración diaria.

Qué hacer cuando faltan herramientas en el contenedor

¿Por qué no hay comandos?

Entras al contenedor, quieres editar con vim:

root@abc123:/# vim /etc/nginx/nginx.conf
bash: vim: command not found

Pruebas con curl:

root@abc123:/# curl localhost:8080
bash: curl: command not found

Ni siquiera hay ping. La primera vez es desconcertante.

Es la filosofía de las imágenes Docker: solo lo imprescindible. Una Ubuntu completa puede pesar cientos de MB; Alpine, unos 5 MB. ¿Cómo? Quitando vim, curl, ping y otras herramientas que damos por hechas.

Instalar herramientas al vuelo

Según la base del contenedor:

Debian/Ubuntu (apt/apt-get):

# Actualizar índice de paquetes
apt-get update

# Herramientas habituales
apt-get install -y vim curl wget net-tools

# Diagnóstico de red
apt-get install -y iputils-ping dnsutils

CentOS/RedHat (yum):

yum install -y vim curl wget

Alpine (apk):

apk update
apk add vim curl bash

¿Qué sistema usa el contenedor?

Dos formas rápidas:

# Método 1: archivo de distribución
cat /etc/os-release

# Método 2: comprobar gestor de paquetes
which apt-get   # Debian/Ubuntu
which yum      # CentOS/RedHat
which apk      # Alpine

Yo suelo probar apt-get update y, si falla, cambio de gestor. No rompe nada.

¿Es seguro hacerlo?

Puntos a tener en cuenta:

Entorno de desarrollo: instala lo que necesites; al reiniciar el contenedor se resetea.

Producción: solo en emergencias. Luego añade las herramientas al Dockerfile y reconstruye.

¿Por qué?

  1. Seguridad: paquetes temporales pueden traer vulnerabilidades
  2. Reproducibilidad: al reiniciar desaparecen y hay que repetir el proceso

Solución a largo plazo: preinstalar en el Dockerfile

Si depuras a menudo, instala en build:

FROM nginx:latest

# Herramientas de depuración
RUN apt-get update && apt-get install -y \
    vim \
    curl \
    wget \
    net-tools \
    iputils-ping \
 && rm -rf /var/lib/apt/lists/*  # Limpia caché y reduce tamaño

# Resto de configuración...

El rm -rf /var/lib/apt/lists/* al final reduce bastante el tamaño de la imagen.

Truco práctico

Para leer config no hace falta vim. cat o less suelen existir:

# Archivo completo
cat /etc/nginx/nginx.conf

# Paginado (q para salir)
less /etc/nginx/nginx.conf

# Primeras líneas
head -n 20 /etc/nginx/nginx.conf

Sin vim, sed puede salvar en apuros:

# Cambiar listen 80 por listen 8080
sed -i 's/listen 80/listen 8080/g' /etc/nginx/nginx.conf

Entrar al contenedor como un usuario concreto

¿Por qué especificar usuario?

A veces entras y ves:

root@abc123:/app# cat /var/log/app.log
cat: /var/log/app.log: Permission denied

O al cambiar permisos:

root@abc123:/app# chmod 644 config.yaml
chmod: changing permissions of 'config.yaml': Operation not permitted

El prompt dice root, pero no tienes permisos. Suele pasar cuando el contenedor corre como usuario no root (buena práctica en producción) y exec hereda esa configuración.

Entrar como root

Especifica root explícitamente:

docker exec -it --user root my-app bash

O la forma corta:

docker exec -it -u root my-app bash

Ahora sí tienes permisos root reales.

Entrar con un UID concreto

Si la app corre como UID 1000:

# UID 1000
docker exec -it --user 1000 my-app bash

# Por nombre de usuario (si existe en el contenedor)
docker exec -it --user appuser my-app bash

# Usuario y grupo (usuario:grupo)
docker exec -it --user 1000:1000 my-app bash

Muy útil para depurar permisos:

# Como usuario de la aplicación
docker exec -it --user appuser my-app bash

# Probar escritura en /data
appuser@abc123:/app$ touch /data/test.txt
touch: cannot touch '/data/test.txt': Permission denied

# Ahí está el problema

¿Cuándo hace falta root?

En mi experiencia, suele hacer falta para:

  1. Config del sistema: archivos bajo /etc/
  2. Instalar paquetes: apt-get, yum, etc.
  3. Logs del sistema: muchos en /var/log/
  4. Permisos: chmod, chown
  5. Red: tcpdump, netstat suelen requerir root

Pero evita root si puedes. En producción, documenta lo que cambies.

Recordatorio de seguridad

Entorno de pruebas: sin gran problema; al reiniciar se resetea.

Producción:

  • Mirar archivos → OK
  • Cambio temporal de config → aceptable si lo registras
  • Compilar o instalar software dentro del contenedor en caliente → no; va al Dockerfile

Los contenedores son infraestructura inmutable: lo que cambies en runtime se pierde al reiniciar. Los cambios reales deben estar en el Dockerfile.

Además, root del contenedor puede coincidir con UID 0 del host según la configuración. Por eso en producción se recomienda usuario no root.

Trucos prácticos y escenarios habituales

Ejecutar un solo comando (sin shell interactivo)

Muchas veces no necesitas quedarte dentro; solo un comando:

# Listar directorio
docker exec my-nginx ls -la /etc/nginx/

# Ver configuración
docker exec my-nginx cat /etc/nginx/nginx.conf

# Procesos
docker exec my-nginx ps aux

# Puertos en escucha
docker exec my-nginx netstat -tlnp

# Probar conectividad
docker exec my-nginx curl -I localhost:80

Ideal para scripts o comprobaciones rápidas:

# Health check
if docker exec my-app curl -f http://localhost:8080/health; then
  echo "App is healthy"
else
  echo "App is down!"
fi

Flujo estándar de depuración

Cuando algo falla, suelo seguir este orden:

1. Estado del contenedor

docker ps -a  # ¿Está corriendo?

2. Logs

docker logs my-app --tail 100  # Últimas 100 líneas
docker logs my-app -f          # Seguimiento en tiempo real

3. Procesos dentro del contenedor

docker exec my-app ps aux

Comprueba que los procesos esperados existen (p. ej. nginx master y workers).

4. Puertos

docker exec my-app netstat -tlnp
# Si no hay netstat:
docker exec my-app ss -tlnp

5. Disponibilidad del servicio

# Desde dentro
docker exec my-app curl localhost:8080

# Sin curl, telnet al puerto
docker exec my-app telnet localhost 8080

6. Configuración

docker exec my-app cat /etc/nginx/nginx.conf
docker exec my-app cat /app/config.yaml

7. Espacio en disco

docker exec my-app df -h

A veces el disco lleno impide escribir logs o datos.

Combinaciones útiles

Variables de entorno:

docker exec my-app env | grep DATABASE

Buscar archivos:

docker exec my-app find /app -name "*.log"

Permisos:

docker exec my-app ls -la /app/

Recursos en tiempo real (en el host, sin exec):

docker stats my-app

CPU, memoria, red e I/O en directo.

Varios contenedores a la vez

Con un script shell:

# Memoria de todos los contenedores en ejecución
for container in $(docker ps -q); do
  echo "Container: $container"
  docker exec $container free -h
  echo "---"
done

Depurar red

Entre contenedores:

# Ping desde A a B
docker exec containerA ping containerB

# Salida a internet
docker exec my-app ping -c 3 google.com

# DNS
docker exec my-app nslookup google.com

IP del contenedor:

docker inspect my-app | grep IPAddress
# O desde dentro:
docker exec my-app ip addr show

¿Contenedor que reinicia sin parar?

Si cae antes de poder hacer exec:

Método 1: sobrescribir el comando de arranque

# Mantener el contenedor vivo sin ejecutar el CMD original
docker run -it --name debug-app my-app-image sh

Método 2: contenedor ya parado

docker start my-app
docker logs my-app
# Si sigue vivo, exec rápido:
docker exec -it my-app bash

Estos trucos los fui acumulando en producción. Al principio cuesta; con práctica, depurar contenedores va mucho más rápido.

Conclusión

La depuración de contenedores Docker se resume en pocos puntos:

Usa docker exec para entrar con seguridad; al salir el contenedor sigue. Comando base: docker exec -it nombre-contenedor bash; si no hay bash, prueba sh.

No confundas exec y attach; attach enlaza al proceso principal y al salir puede parar el contenedor. Salvo que sepas exactamente qué haces, quédate con exec.

Si faltan herramientas, instálalas al vuelo (apt-get/yum/apk), pero es parche; lo durable es el Dockerfile.

Para root, añade --user root, con cuidado en producción y documentando cambios.

Sigue un flujo: logs → procesos → configuración → red. Resuelve la mayoría de incidencias.

La próxima vez, antes de reiniciar a ciegas, entra y mira: muchas veces es config, permisos o puertos.

Lista de comprobación:

  • ¿El contenedor corre? (docker ps)
  • ¿Hay errores en logs? (docker logs)
  • ¿Los procesos están activos? (docker exec ps aux)
  • ¿Los puertos escuchan? (docker exec netstat -tlnp)
  • ¿La configuración es correcta? (docker exec cat config)
  • ¿Hay espacio en disco? (docker exec df -h)
  • ¿La red responde? (docker exec curl/ping)

Repasa esta lista y suele bastar para acotar el problema.

Flujo completo de depuración de contenedores Docker

Pasos completos para depurar contenedores con exec: comandos, instalación de herramientas, permisos y método sistemático

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Entrar al contenedor: entender exec y attach

    Diferencia esencial entre exec y attach: exec crea un proceso nuevo dentro del contenedor (recomendado, no afecta al principal); attach se conecta al stdin/stdout del proceso principal (no recomendado, al salir detiene el contenedor).

    Comandos básicos:
    • docker exec -it nombre-contenedor /bin/bash (entrar con bash)
    • docker exec -it nombre-contenedor sh (entrar con sh, habitual en Alpine)
    • docker exec nombre-contenedor comando (ejecutar un solo comando sin entrar)

    Parámetros:
    • -i: mantiene STDIN abierto (modo interactivo)
    • -t: asigna un pseudo-terminal (salida formateada)
    • Combinar ambos da el mejor resultado

    Cuándo usar cada uno: exec para depurar, instalar herramientas o modificar configuración; attach solo para ver la salida en tiempo real.
  2. 2

    Step 2: Resolver herramientas faltantes y problemas de permisos

    Instalación de herramientas:
    • Debian/Ubuntu: apt-get update && apt-get install -y nombre-herramienta
    • Alpine: apk add --no-cache nombre-herramienta
    • CentOS/RHEL: yum install -y nombre-herramienta

    Sistema de archivos de solo lectura: si el contenedor es de solo lectura, primero ejecuta:
    docker exec -u root nombre-contenedor sh -c 'mount -o remount,rw /'

    Especificar usuario:
    • -u root para permisos root: docker exec -u root nombre-contenedor
    • --user uid:gid para un usuario concreto
    • Comprobar usuario actual: whoami
    • Ver permisos: ls -la
    • Modificar permisos: chmod 755 archivo, chown usuario:grupo archivo

    Precaución: en producción usa root con cuidado; documenta los cambios y sincronízalos al Dockerfile para no perderlos al reiniciar.
  3. 3

    Step 3: Flujo sistemático de comprobaciones de depuración

    Lista de comprobación (en este orden):
    1) Ver logs: docker logs nombre-contenedor, o dentro del contenedor /var/log
    2) Comprobar procesos: ps aux | grep nombre-proceso
    3) Verificar puertos: netstat -tuln | grep puerto, o ss -tuln
    4) Revisar configuración: cat /ruta/config
    5) Espacio en disco: df -h
    6) Conectividad de red: curl http://localhost:puerto, ping hostname

    Flujo: logs para localizar errores → procesos para confirmar el servicio → configuración → red

    Con este orden se resuelven más del 90 % de los problemas de contenedores.

FAQ

¿Por qué se recomienda exec en lugar de attach para entrar al contenedor?
La diferencia clave está en el manejo de procesos:

• exec: crea un proceso nuevo; no afecta al principal y el contenedor sigue al salir
• attach: se conecta al stdin/stdout del proceso principal; al salir detiene el contenedor

Por eso exec encaja mejor en depuración; attach sirve sobre todo para ver salida en tiempo real.
¿Qué hacer si no hay bash ni sh en el contenedor?
Si no hay bash ni sh, prueba:

• Otro shell: docker exec -it nombre-contenedor /bin/ash (habitual en Alpine)
• Ejecutar comando directo: docker exec nombre-contenedor comando
• Listar shells: docker exec nombre-contenedor ls /bin/
• busybox: docker exec -it nombre-contenedor busybox sh

Para depuración interactiva, conviene instalar bash o sh en el Dockerfile.
¿Cómo instalar herramientas en un contenedor con sistema de archivos de solo lectura?
Primero remonta la raíz como escribible:

1. docker exec -u root nombre-contenedor sh -c 'mount -o remount,rw /'

2. Instala herramientas:
docker exec -u root nombre-contenedor apt-get update
docker exec -u root nombre-contenedor apt-get install -y nombre-herramienta

Los cambios se pierden al reiniciar; añade la instalación al Dockerfile o usa build multietapa.
¿Qué hacer si el contenedor no puede acceder a la red externa?
Pasos para problemas de red:

1. Modo de red:
docker inspect nombre-contenedor | grep NetworkMode

2. DNS:
docker exec nombre-contenedor nslookup google.com

3. Conectividad:
docker exec nombre-contenedor ping -c 3 8.8.8.8

4. Firewall:
docker exec nombre-contenedor iptables -L

5. Red personalizada:
docker network inspect nombre-red

Causas habituales: DNS mal configurado, reglas de firewall o modo de red incorrecto.
¿Cómo salir del contenedor con exec sin detenerlo?
Formas seguras de salir:

• exit o Ctrl+D: salida normal, el contenedor sigue
• Ctrl+P y luego Ctrl+Q: modo detach
• Cerrar la terminal con -it también es válido

No uses attach y luego Ctrl+C: eso detiene el proceso principal. Con exec, salir no afecta al proceso principal.

10 min de lectura · Publicado el: 18 dic 2025 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog