With Docker's default json-file logging driver on Linux, each container's stdout and stderr are stored at /var/lib/docker/containers/<container-id>/<container-id>-json.log, where <container-id> is the full 64-character ID. In day-to-day use you read them with docker logs <container> rather than touching the file. On Docker Desktop (Mac/Windows) that path lives inside the Linux VM, not on your host, so docker logs is the only reliable way to read them. The driver is configurable globally in /etc/docker/daemon.json or per container with --log-driver.
Quick Reference: Docker Log Locations by Driver
| Logging driver | Where logs go | Readable with docker logs? |
|---|---|---|
json-file (default) | /var/lib/docker/containers/<id>/<id>-json.log | Yes |
local | /var/lib/docker/containers/<id>/local-logs/ (binary, rotated) | Yes |
journald | systemd journal (journalctl CONTAINER_NAME=<name>) | Yes |
syslog | host/remote syslog daemon | No |
fluentd | Fluentd collector endpoint | No |
awslogs | Amazon CloudWatch Logs | No |
gelf | Graylog/Logstash endpoint | No |
none | discarded | No |
Host-by-host data directory:
| Platform | Docker data root |
|---|---|
| Native Linux | /var/lib/docker/ |
| Docker Desktop (Mac) | inside the VM (~/Library/Containers/com.docker.docker/Data/...) |
| Docker Desktop (Windows, WSL2) | inside the WSL distro / VM disk |
| Rootless Docker | ~/.local/share/docker/ |
How to View and Tail Docker Logs
Follow a container's live output:
docker logs -f <container>
If this returns Cannot connect to the Docker daemon rather than any log lines, the problem is not your logging setup — the CLI cannot reach the engine at all, and no docker command will work until the daemon is running.
Show the last 100 lines with timestamps:
docker logs --tail 100 -t <container>
Only logs since a relative time:
docker logs --since 15m <container>
docker logs --since 2026-06-01T00:00:00 <container>
Compose stacks aggregate logs across services:
docker compose logs -f
docker compose logs -f web db
Find the raw file path on Linux:
docker inspect --format '{{.LogPath}}' <container>
Read the raw json-file directly (root required) and pretty-print:
sudo cat /var/lib/docker/containers/<id>/<id>-json.log | jq -r '.log'
For the journald driver:
journalctl CONTAINER_NAME=<name> -f
Finding Which Driver a Container Uses
The global default:
docker info --format '{{.LoggingDriver}}'
The driver for a specific container (it may differ from the default):
docker inspect --format '{{.HostConfig.LogConfig.Type}}' <container>
If this returns none or syslog/fluentd/awslogs, then docker logs will not work and you must read the destination system instead.
Native Linux Detail
Everything sits under /var/lib/docker/containers/. Each subdirectory is named after the full container ID and contains <id>-json.log plus rotated copies like <id>-json.log.1. These files are owned by root, so reading them directly requires sudo. Deleting a container with docker rm removes its log directory.
Docker Desktop Detail
Because the engine runs in a VM, /var/lib/docker is not visible from macOS Finder or Windows Explorer. To inspect the VM directly on Mac:
docker run -it --rm --privileged --pid=host justincormack/nsenter1
ls /var/lib/docker/containers/
In practice, just use docker logs — it proxies into the VM for you.
Changing the Logging Driver
Set the engine-wide default and rotation in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Apply with sudo systemctl restart docker. Existing containers keep their old config until recreated. Override per container at run time:
docker run --log-driver local --log-opt max-size=20m --log-opt max-file=5 myimage
In Compose:
services:
web:
image: myimage
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Log Rotation
The json-file driver does not rotate logs unless you set max-size/max-file; a single chatty container can fill /var/lib/docker and take down the host, at which point every write on the box starts failing with no space left on device and Docker itself often refuses to start containers. The local driver rotates and compresses automatically (default 20 MB, 5 files). You can also configure host logrotate, but the cleanest fix is max-size/max-file in daemon.json. Inspect current usage:
sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail
Common Errors You'll Find in Docker Logs
Error: configured logging driver does not support reading— the container usessyslog,fluentd,gelf, orawslogs; read the destination system, notdocker logs.standard_init_linux.go: exec user process caused: no such file or directory— wrong architecture image or a missing interpreter/shebang in the entrypoint.OCI runtime create failed: ... permission denied— a mounted file or entrypoint script is not executable, or SELinux/AppArmor blocked it.no space left on device— the disk holding/var/lib/dockeris full, frequently from unrotated json logs or dangling images.container ... is not running— you randocker exec/logs -fagainst a container that already exited; checkdocker ps -aand exit codes.OOMKilledindocker inspectstate — the container exceeded its memory limit and the kernel killed it. The same condition on Kubernetes surfaces as CrashLoopBackOff with exit code 137, where you read the crashed run withkubectl logs --previousinstead.
Troubleshooting: Logs Missing or Empty
- App logs to a file, not stdout —
docker logsonly captures stdout/stderr. Apps writing to a file inside the container won't appear; reconfigure the app to log to stdout or mount the file out. - Non-readable driver —
docker logserrors when the driver issyslog/fluentd/awslogs/none; checkHostConfig.LogConfig.Type. - Container removed —
--rmordocker rmdeletes the json file; the logs are unrecoverable. - Rotation pruned them —
max-filerotation discarded older lines; only the most recent files remain. - Permissions — reading
/var/lib/docker/containers/.../-json.logdirectly needs root. - Docker Desktop — the file path doesn't exist on the host; use
docker logs. - Buffered output — some runtimes buffer stdout; set
PYTHONUNBUFFERED=1(Python) or disable buffering so lines flush promptly.