--- title: "Logs de Docker: leerlos, rotarlos y depurar contenedores" description: "Aprende a leer los logs de tus contenedores con docker logs, limitar su tamaño en daemon.json y depurar contenedores que fallan, se reinician o no conectan." category: "Docker Kubernetes" language: es source: https://cubepath.com/es/docs/docker-kubernetes/logs-de-docker-y-solucion-de-problemas updated: 2026-09-25 --- # Logs de Docker: leerlos, rotarlos y depurar contenedores Docker guarda todo lo que un contenedor escribe en su salida estándar (stdout) y de error (stderr), y te lo devuelve con `docker logs`. En este tutorial aprenderás a consultar esos logs de forma eficaz, a evitar que llenen el disco con la rotación de logs y a seguir un método ordenado para depurar contenedores que no arrancan, se reinician en bucle o no llegan a la red. Los comandos son para Ubuntu 24.04 con Docker Engine instalado desde el repositorio oficial. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker. - Tu usuario en el grupo `docker`, o anteponer `sudo` a cada comando `docker`. Comprueba la versión de Docker y el driver de logs que usa el daemon: ```bash docker version --format '{{.Server.Version}}' docker info --format '{{.LoggingDriver}}' ``` ```output 28.5.1 json-file ``` El número de versión que veas será el que tengas instalado. Lo importante es la segunda línea: `json-file` es el driver por defecto. ## Paso 1: Crear un contenedor de prueba Para practicar necesitas un contenedor que genere logs. Arranca Nginx publicando el puerto 8080 solo en localhost: ```bash docker run -d --name web -p 127.0.0.1:8080:80 nginx:stable ``` Genera un par de peticiones, una válida y otra que devuelva 404, para tener algo que leer: ```bash curl -s -o /dev/null http://127.0.0.1:8080/ curl -s -o /dev/null http://127.0.0.1:8080/no-existe ``` La imagen oficial de Nginx redirige su log de acceso a stdout y su log de errores a stderr, así que ambos aparecerán en `docker logs`. Si tu aplicación escribe sus logs en un archivo dentro del contenedor, Docker no los verá: configúrala para que escriba en stdout/stderr. ## Paso 2: Leer los logs con docker logs Muestra todo el log del contenedor: ```bash docker logs web ``` En la salida verás los mensajes de arranque de Nginx y dos líneas de acceso, una con código 200 y otra con 404. En un contenedor con semanas de actividad eso son miles de líneas. Estas opciones limitan la salida: | Opción | Qué hace | |---|---| | `--tail 100` | Solo las últimas 100 líneas | | `-f` | Sigue el log en tiempo real (sal con `Ctrl+C`) | | `-t` | Añade la marca de tiempo de Docker a cada línea | | `--since 30m` | Líneas de los últimos 30 minutos (acepta `s`, `m`, `h` o una fecha RFC 3339) | | `--until 2026-09-25T12:00:00` | Líneas anteriores a esa fecha | Lo más habitual al investigar un problema es combinar varias: ```bash docker logs -f -t --tail 50 web ``` Para buscar texto con `grep` recuerda que `docker logs` reproduce stderr como stderr. Redirígelo a stdout o `grep` no lo verá: ```bash docker logs --since 1h web 2>&1 | grep -i ' 404 ' ``` ```output 172.17.0.1 - - [25/Sep/2026:10:14:02 +0000] "GET /no-existe HTTP/1.1" 404 153 "-" "curl/8.5.0" "-" ``` ### Logs de un proyecto de Docker Compose Con Compose no necesitas saber el nombre de cada contenedor. Desde el directorio del proyecto (donde está `compose.yaml`) usa: ```bash docker compose logs -f --tail 50 ``` Añade el nombre de uno o varios servicios al final para filtrar, por ejemplo `docker compose logs -f api db`. Cada línea va prefijada con el nombre del contenedor, lo que ayuda a seguir una petición entre servicios. ### Dónde se guardan los logs Con el driver `json-file`, cada contenedor tiene su archivo en `/var/lib/docker/containers//-json.log`. Puedes ver la ruta exacta y su tamaño así: ```bash sudo ls -lh "$(docker inspect --format '{{.LogPath}}' web)" ``` No edites ni vacíes ese archivo a mano mientras el contenedor está en marcha: para controlar su tamaño usa la rotación del paso siguiente. ## Paso 3: Configurar la rotación de logs Por defecto `json-file` no tiene límite de tamaño: un contenedor muy hablador puede llenar el disco del servidor. Configura límites para todos los contenedores en `/etc/docker/daemon.json`: ```bash sudo nano /etc/docker/daemon.json ``` Si el archivo no existe, créalo con este contenido. Si ya existe, añade las claves respetando el JSON que tenga: ```json { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } ``` Con esto cada contenedor guarda como máximo tres archivos de 10 MB. Valida el JSON antes de reiniciar, porque un error de sintaxis impide que Docker arranque: ```bash sudo dockerd --validate --config-file /etc/docker/daemon.json ``` ```output configuration OK ``` Reinicia el daemon para aplicar el cambio: ```bash sudo systemctl restart docker ``` > Importante: la configuración de logs solo se aplica a contenedores creados después del cambio. Los existentes conservan la que tenían. Recréalos con `docker rm -f` y `docker run`, o con `docker compose up -d --force-recreate` en proyectos de Compose. Recrea el contenedor de prueba y comprueba que ha recibido los límites: ```bash docker rm -f web docker run -d --name web -p 127.0.0.1:8080:80 nginx:stable docker inspect --format '{{json .HostConfig.LogConfig}}' web ``` ```output {"Type":"json-file","Config":{"max-file":"3","max-size":"10m"}} ``` Si prefieres fijar los límites por servicio en Compose, usa el bloque `logging`: ```yaml services: web: image: nginx:stable logging: driver: json-file options: max-size: "10m" max-file: "3" ``` > Nota: Docker también ofrece el driver `local`, que rota por defecto y guarda los logs comprimidos, y el driver `journald`, que envía los logs al journal de systemd (consultables con `journalctl CONTAINER_NAME=web`). Ambos siguen funcionando con `docker logs`. Para enviar logs a un sistema centralizado (syslog remoto, Fluentd, Loki) consulta la documentación del driver correspondiente. ## Paso 4: Averiguar por qué un contenedor se ha parado Cuando un contenedor no está en marcha, empieza siempre por su estado. `docker ps -a` muestra también los contenedores parados y cómo terminaron: ```bash docker ps -a --filter name=web ``` Para simular un fallo, lanza un contenedor cuyo proceso termina con error: ```bash docker run --name falla alpine:3 sh -c 'echo "no encuentro la configuración" >&2; exit 3' ``` Los logs de un contenedor parado siguen disponibles hasta que lo borras: ```bash docker logs falla ``` ```output no encuentro la configuración ``` Consulta el estado completo, incluyendo el código de salida y si el kernel lo mató por falta de memoria: ```bash docker inspect --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} restarts={{.RestartCount}}' falla ``` ```output status=exited exit=3 oom=false restarts=0 ``` Los códigos de salida más útiles para diagnosticar: | Código | Significado habitual | |---|---| | `0` | El proceso terminó correctamente. Si el contenedor debería seguir vivo, su comando no es un servicio en primer plano | | `1` o propio de la app | Error de la aplicación: la causa está en `docker logs` | | `125` | Docker no pudo crear el contenedor (opción de `docker run` incorrecta) | | `126` | El comando existe pero no se puede ejecutar (permisos, formato) | | `127` | El comando no existe en la imagen | | `137` | Recibió SIGKILL: por falta de memoria (`OOMKilled=true`) o por `docker kill` | | `143` | Recibió SIGTERM, normalmente por un `docker stop` | Si el contenedor tiene una política de reinicio (`--restart unless-stopped`), un fallo se convierte en un bucle: `docker ps` mostrará `Restarting (1) 5 seconds ago` y `RestartCount` irá subiendo. En ese caso lee los logs anteriores al último reinicio con `docker logs --tail 100 nombre`. Borra el contenedor de prueba: ```bash docker rm falla ``` ## Paso 5: Inspeccionar un contenedor en ejecución Cuando el contenedor funciona pero se comporta mal, entra en él. Abre una shell (usa `sh` si la imagen no incluye `bash`): ```bash docker exec -it web sh ``` Algunas comprobaciones útiles sin entrar en la shell: ```bash docker exec web env docker exec web cat /etc/nginx/conf.d/default.conf docker top web ``` `docker top` lista los procesos del contenedor vistos desde el host. Para ver el consumo de CPU, memoria, red y disco de todos los contenedores en una sola captura: ```bash docker stats --no-stream ``` ```output CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 3f1c2a9d7b21 web 0.00% 7.9MiB / 3.82GiB 0.20% 1.2kB / 0B 0B / 8kB 3 ``` Si la memoria está cerca del límite y el contenedor acaba con `OOMKilled=true`, sube el límite con `--memory` o reduce el consumo de la aplicación. ### Depurar la red de un contenedor Muchas imágenes no incluyen `ping`, `curl` ni `dig`. En lugar de instalarlos dentro, arranca un contenedor de herramientas que comparta el espacio de red del contenedor a depurar. La imagen `nicolaka/netshoot` trae las habituales: ```bash docker run --rm -it --network container:web nicolaka/netshoot ``` Dentro de ese contenedor, `localhost` es el propio contenedor `web`, así que puedes probar su puerto, su DNS y su salida a Internet: ```bash curl -sI http://localhost:80 dig +short cubepath.com ``` Para conocer la IP de un contenedor en cualquier red (la plantilla funciona también con redes definidas por el usuario): ```bash docker inspect --format '{{range $net, $cfg := .NetworkSettings.Networks}}{{$net}} {{$cfg.IPAddress}}{{"\n"}}{{end}}' web ``` ```output bridge 172.17.0.2 ``` Recuerda que los contenedores de una misma red definida por el usuario (por ejemplo, la que crea Compose) se resuelven por nombre de servicio; en la red `bridge` por defecto no hay resolución por nombre. ## Paso 6: Revisar eventos y logs del daemon Si el problema no está en la aplicación sino en Docker (el contenedor no se crea, un volumen no se monta, la red falla), mira el daemon. `docker events` muestra en tiempo real lo que ocurre: creación, arranque, muertes, OOM, health checks: ```bash docker events --since 30m --until "$(date +%s)" --filter type=container ``` `--until` con la hora actual hace que el comando devuelva el histórico y termine en lugar de quedarse esperando. Añade `--filter container=web` para centrarte en un contenedor. Los logs del propio daemon están en el journal de systemd: ```bash sudo journalctl -u docker.service --since "1 hour ago" --no-pager ``` Si necesitas más detalle, añade `"debug": true` a `/etc/docker/daemon.json` y recarga la configuración sin parar los contenedores: ```bash sudo systemctl reload docker ``` Desactívalo al terminar, porque el modo debug genera mucho volumen. ## Solución de problemas **`Bind for 0.0.0.0:8080 failed: port is already allocated`.** Otro proceso o contenedor usa ese puerto. Búscalo: ```bash sudo ss -ltnp 'sport = :8080' docker ps --filter publish=8080 ``` Para el servicio que lo ocupa o publica el contenedor en otro puerto (`-p 8081:80`). **El contenedor sale nada más arrancar con código 0.** El comando de la imagen termina en lugar de quedarse en primer plano (un script que lanza un demonio en segundo plano, o una imagen base como `ubuntu` sin comando). Revisa el `CMD` con `docker inspect --format '{{.Config.Cmd}}' nombre` y haz que el proceso principal se quede en primer plano. **Quiero ver qué hay dentro de una imagen que no arranca.** Sustituye el punto de entrada por una shell para explorar el sistema de archivos: ```bash docker run --rm -it --entrypoint sh nombre_imagen ``` **`permission denied` al escribir en un volumen.** El usuario del contenedor no tiene permisos sobre el directorio montado. Averigua con qué UID se ejecuta (`docker exec nombre id`) y ajusta el propietario del directorio en el host con `sudo chown` a ese UID. No uses `chmod 777`. **No hay espacio en disco.** Mira qué ocupa Docker y limpia lo que no se use: ```bash docker system df docker image prune docker builder prune ``` `docker system prune -a --volumes` borra también imágenes no usadas y volúmenes sin contenedor: úsalo solo si sabes que esos volúmenes no contienen datos que necesites. Si lo que crece son los logs, aplica la rotación del paso 3. **El contenedor no resuelve nombres DNS.** Comprueba su `/etc/resolv.conf` con `docker exec nombre cat /etc/resolv.conf` y prueba la resolución desde `netshoot`. Si el servidor usa un resolver local en `127.0.0.53` (systemd-resolved), Docker ya lo sustituye por los servidores reales; si aun así falla, fija DNS explícitos con la clave `"dns": ["1.1.1.1", "9.9.9.9"]` en `daemon.json`. ## Conclusión Ya sabes leer y filtrar los logs de tus contenedores, limitar su tamaño para que no llenen el disco y seguir un orden de diagnóstico: estado y código de salida, logs, inspección en vivo, red y, por último, el daemon. Con eso se resuelven la mayoría de incidencias del día a día. Como siguientes pasos puedes: - Añadir una instrucción `HEALTHCHECK` a tus imágenes para que `docker ps` muestre si la aplicación responde, no solo si el proceso vive. - Enviar los logs a un sistema centralizado (Loki, Graylog o un syslog remoto) cuando gestiones varios servidores. - Aplicar límites de memoria y CPU a tus contenedores para que un fallo no afecte al resto del servidor.