docker logs

docker logs shows the output written by a container’s main process to standard output and standard error. It matters because most containerized applications are expected to log to the console, then let Docker capture and expose those logs. When a container exits quickly, refuses a connection, or behaves strangely, docker logs is usually the first useful troubleshooting command.

The command does not open a shell inside the container and it does not read arbitrary files from the container filesystem. It asks the Docker daemon for the log stream that Docker already captured from the container process.

Overview: How docker logs Works

A Docker image is a read-only template made from filesystem layers and metadata. A container is an instance of that image with a thin writable layer, runtime configuration, and usually one main process. When you run a container, Docker connects that process’s stdout and stderr streams to Docker’s logging system. docker logs reads from that captured stream for a specific container.

This design is deliberate. In a container, the application should normally write logs to the console instead of hiding them in a file such as /var/log/app.log. Docker, Docker Compose, and container platforms can then collect, rotate, forward, and display logs consistently. A web server image, for example, commonly directs access logs and error logs to stdout and stderr so docker logs can show them.

The Docker CLI is only the client. When you run docker logs web, the CLI sends an API request to the Docker daemon. The daemon finds the container by name or ID, reads logs stored by the container’s configured logging driver, and streams the result back to your terminal. On Docker Desktop for macOS and Windows, Linux containers run inside Docker’s managed Linux VM, but the command works the same way from your host terminal.

By default, Docker Engine uses the json-file logging driver unless configured otherwise. With that driver, Docker stores each log line with metadata on the host. Some environments use a different driver, such as journald, local, syslog, fluentd, or a cloud logging driver. This matters because docker logs is only available for drivers that support reading logs back through Docker. If logs are sent directly to an external service by a non-readable driver, docker logs may not show them.

docker logs works for running and stopped containers as long as the container object still exists and the logging driver kept readable data. Removing a container with docker rm removes its Docker-managed metadata and usually its captured logs. Removing an image does not remove logs from existing containers, because containers and images are separate objects.

Syntax

docker logs [OPTIONS] CONTAINER
Option Meaning
CONTAINER The container name or ID, such as web-logs. Use docker ps -a to find stopped containers.
-f or --follow Stream new log lines as the container writes them, similar to tail -f.
--tail N Show only the last N lines before exiting or before following. Use --tail 50 for a readable recent slice.
-t or --timestamps Prefix each line with the timestamp recorded by Docker.
--since VALUE Show logs after a time value, such as 10m, 1h, or an RFC3339 timestamp.
--until VALUE Show logs before a time value. Useful with --since when investigating a known incident window.
--details Show extra attributes recorded by the logging driver, when available.

The common troubleshooting pattern is docker logs --tail 100 CONTAINER for recent history, then docker logs -f CONTAINER when you want to watch new events arrive.

Examples

Example 1: Read Logs from a Running Web Container

docker run -d --name web-logs -p 8080:80 nginx:1.27-alpine
docker logs web-logs

Output:

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/08/03 12:00:00 [notice] 1#1: using the "epoll" event method
2026/08/03 12:00:00 [notice] 1#1: start worker processes

The first command starts Nginx in detached mode. The second asks Docker for the log output captured from that container. The exact lines can differ by image version, platform, and configuration, but the important point is that Nginx wrote to console streams and Docker kept those lines for later reading.

Example 2: Follow Logs as New Lines Arrive

docker logs --follow --tail 20 web-logs

Output:

2026/08/03 12:00:00 [notice] 1#1: start worker process 30
2026/08/03 12:00:00 [notice] 1#1: start worker process 31

--tail 20 prints only the most recent 20 lines first, which prevents a huge backlog from flooding your terminal. --follow then keeps the command attached so new lines appear as the container writes them. Press Ctrl+C to stop watching; this stops the log command, not the container.

Example 3: Add Timestamps and a Time Window

docker logs --timestamps --since 10m --tail 50 web-logs

Output:

2026-08-03T12:00:00.123456789Z 2026/08/03 12:00:00 [notice] 1#1: using the "epoll" event method
2026-08-03T12:00:00.223456789Z 2026/08/03 12:00:00 [notice] 1#1: start worker processes

This is a practical incident command. It limits the log search to roughly the last ten minutes, caps the display at 50 lines, and adds Docker’s recorded timestamps. The timestamp prefix is especially useful when the application log message itself lacks a reliable time.

Example 4: Inspect a Container That Exited Immediately

docker run --name broken-command alpine:3.20 sh -c "echo starting; echo missing config >&2; exit 2"
docker ps -a --filter "name=broken-command"
docker logs broken-command

Output:

starting
missing config
CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS                      PORTS     NAMES
2a7f4c8e921a   alpine:3.20   "sh -c 'echo start...'"   4 seconds ago    Exited (2) 3 seconds ago              broken-command
starting
missing config

The container exits with code 2, so it will not appear in plain docker ps. docker ps -a shows that it exists and exited. docker logs then shows both normal output and error output captured from the process. This pattern is the fastest way to debug containers that start and immediately stop.

How It Works Step by Step

  1. You create or start a container. Docker starts the configured main process from the image, plus any command override passed to docker run.
  2. Docker connects the process’s stdout and stderr to its logging subsystem. The application does not need to know where Docker stores the captured lines.
  3. The active logging driver records the stream. With the default readable setup, Docker can later return these records to the CLI.
  4. You run docker logs CONTAINER. The CLI contacts the Docker daemon and identifies the container by name or ID.
  5. The daemon reads the stored records, applies filters such as --since, --until, and --tail, and returns matching lines.
  6. If you use --follow, Docker sends the existing selected lines first, then keeps the stream open and forwards new lines until you interrupt the command or the connection ends.

Notice what Docker does not do here: it does not inspect every file inside the container, and it does not require the container to be running. The log stream belongs to the container object. Once that object is removed, those Docker-managed logs are normally gone too.

Common Mistakes

Expecting docker logs to Read Log Files Inside the Container

docker logs /var/log/nginx/access.log

This is wrong because docker logs takes a container name or ID, not a file path. The fix is to ask for the container’s captured console output:

docker logs web-logs

If an application writes only to a file inside the container, Docker may capture nothing useful. Configure the application to write logs to stdout and stderr, or use an image that already follows that convention.

Forgetting That Plain docker ps Hides Exited Containers

docker ps
docker logs broken-command

If docker ps shows no row, the container may still exist in an exited state. Use docker ps -a to find it:

docker ps -a --filter "name=broken-command"
docker logs broken-command

Letting Logs Grow Without a Rotation Plan

docker run -d --name noisy-app alpine:3.20 sh -c "while true; do date; sleep 1; done"

This command is valid, but it produces a line every second forever. On a real host, long-running noisy containers can consume disk space if the logging driver is not configured with rotation or external collection. The application should log at appropriate levels, and production Docker hosts should use a logging setup with size limits, retention, or forwarding.

Using latest When Reproducing a Log Issue

docker run -d --name web-latest nginx:latest

latest is a moving tag. If you are debugging logs from a specific incident, use the same explicit image tag or digest that produced the behavior. For tutorials and repeatable tests, prefer a pinned tag:

docker run -d --name web-pinned nginx:1.27-alpine

Best Practices

  • Design containers so applications write logs to stdout and stderr, not only to private files inside the container.
  • Use container names with --name so commands like docker logs web-logs are readable.
  • Start troubleshooting failed starts with docker ps -a, then docker logs CONTAINER.
  • Use --tail before --follow to avoid dumping thousands of old lines into your terminal.
  • Use --timestamps, --since, and --until when investigating time-bound incidents.
  • Configure log rotation or centralized logging for long-running Docker hosts. Captured logs can consume disk space.
  • Do not bake secrets into image layers or print secrets in logs. A later cleanup command does not erase secrets from earlier image layers, and logs may be retained outside the container.
  • Use specific image tags such as nginx:1.27-alpine and alpine:3.20 for repeatable debugging.
  • For Compose projects, use docker compose logs when you want service-level logs across several containers.

Practice Exercises

  1. Run an nginx:1.27-alpine container named practice-logs, then use docker logs to view its startup output. Expected end state: you can see Nginx entrypoint or startup lines.
  2. Use a command that prints only the last five log lines from practice-logs and keeps following new lines. Hint: combine --tail and --follow.
  3. Create a short-lived alpine:3.20 container that prints one line to normal output, one line to error output, and exits with a nonzero code. Then find it with docker ps -a and read its logs.

Summary

  • docker logs reads captured stdout and stderr for one container.
  • It works for running and stopped containers as long as the container object and readable logs still exist.
  • --follow streams new lines, --tail limits old output, and --timestamps adds Docker-recorded times.
  • --since and --until help narrow logs to an incident window.
  • The command takes a container name or ID, not a path to a file inside the container.
  • Containers should log to console streams so Docker and other platforms can collect logs consistently.
  • Use explicit image tags and a log rotation or forwarding strategy for reliable, repeatable operations.