`docker logs` prints nothing for a container that exits instantly — how do you get evidence?
answer
- Silence is a fork, not a dead end
- Only the main process stream is captured
- Check whether it ever started
- Stopped containers still yield files
- Override the entrypoint to inspect the image
basics
~20 sEmpty output has three causes: the process never started, it logged to a file instead of stdout, or the log driver cannot be read back. Check the status first, then open the image with docker run --entrypoint sh.
solid answer
~40 sEmpty output is a fork, not a dead end. Check `docker ps -a` first: status `Created` plus a message in `.State.Error` means the runtime never executed the command, so there was never anything to log. If it really ran, the output may be going to a file inside the container — `docker cp <container>:/var/log/app.log .` works on a **stopped** container — or the process may buffer stdout and lose the last lines when it dies. If `docker logs` returns an error about the configured logging driver not supporting reading, the container's log driver ships elsewhere and you must read it there. To interrogate the image itself, override the entrypoint: `docker run --rm -it --entrypoint sh myimage:tag` gives you a shell in the same filesystem, and note that `--entrypoint` also clears the image's `CMD`.
code
bash · 4 linesdocker ps -a --format '{{.Names}} {{.Status}}' --filter name=pdfsign-api
docker inspect --format '{{.State.Error}}' pdfsign-api
docker inspect --format '{{.Path}} {{.Args}}' pdfsign-api
docker inspect --format '{{.HostConfig.LogConfig.Type}}' pdfsign-apigo deeper
Learn that docker logs only shows the main process's stdout and stderr, and that docker run --rm -it --entrypoint sh <image> is the standard way to look around inside an image that will not stay up.
Be able to enumerate the causes of empty output and the command that distinguishes each: status Created versus Exited, the logging driver type, a file inside the container, and buffered output that was never flushed.
Demonstrate ordered elimination under incident pressure, and the discipline of reproducing with the original environment, mounts and user. Say how you would change the service so the next failure is not silent.
Own the standard that makes this rare: applications log to stdout, images ship unbuffered, log drivers and retention are chosen so evidence outlives the incident, and debugging an image does not require weakening how it runs in production.
Silence from `docker logs` is one of the most common ways a container investigation stalls, and it stalls because people read it as "no information" instead of "which of four things is true". The four are: nothing ever ran; something ran but wrote its output elsewhere; something ran and wrote to stdout but the bytes never left the process; or the output exists but not where `docker logs` can read it. Each has a different next command. ## 1. Nothing ever ran `docker ps -a` settles this instantly. Status `Created` means the runtime never executed the container's command, so an empty log is expected rather than mysterious. `docker inspect --format '{{.State.Error}}' <container>` usually returns the runtime's own message — an OCI runtime create failure naming an executable or a path — and the same text was printed by `docker run` at the time. Typical causes: the entrypoint script is not present at the path the image declares; it is present but not executable; it has CRLF line endings so the interpreter line is unusable; the interpreter named in its shebang does not exist in a minimal base image; or a bind-mount source path does not exist on the host. Confirm what the daemon actually tried to run with `docker inspect --format '{{.Path}} {{.Args}}' <container>` — that is the resolved command after ENTRYPOINT and CMD are combined, and it is frequently not what the author assumed. ## 2. It ran, but logged somewhere else Docker captures the **main process's stdout and stderr** and nothing else. An application configured to write to `/var/log/app.log`, or a supervisor that starts children whose output is redirected to files, produces a container that runs and logs nothing visible. The stopped container still has its writable layer, so retrieve the file directly: ``` docker cp pdfsign-api:/var/log/pdfsign.log ./pdfsign.log ``` `docker cp` works on stopped containers, which makes it the right tool for a post-mortem. When you want the whole filesystem, `docker export <container> > fs.tar` gives you a flat tarball of it. ## 3. It ran and wrote to stdout, but the bytes were lost Runtimes buffer stdout when it is a pipe rather than a terminal, and a process that dies abruptly never flushes. A Django application served by gunicorn is a textbook case: the interesting traceback sits in a buffer that is discarded when the worker is killed. The fixes are configuration, not archaeology — set `PYTHONUNBUFFERED=1` in the image for Python, use the runtime's unbuffered flag for other languages, or configure the application's logger to write to stderr, which is conventionally unbuffered. Re-run and the failure narrates itself. ## 4. The output exists, but not where `docker logs` reads `docker logs` reads what the container's logging driver stored locally. With the default `json-file` driver, that is a file on the host and reading works. With a driver that ships logs off the host, `docker logs` may refuse with an error stating that the configured logging driver does not support reading; modern engines mitigate this with a local cache so `docker logs` still works, but that cache can be disabled and it is size-limited. Check the driver with `docker inspect --format '{{.HostConfig.LogConfig.Type}}' <container>` before concluding anything about the application. Rotation is the other half of this: when `max-size` and `max-file` are configured, output older than that window is genuinely gone. ## Interrogating the image itself When the container is too short-lived to catch, stop debugging the container and debug the image. `--entrypoint` replaces the image's declared entrypoint for one run: ``` docker run --rm -it --entrypoint sh pdfsign:1.9.3 ``` You land in the same filesystem the failing process saw, with the same environment defaults, and can check the facts that the crash depended on: does the entrypoint script exist and is it executable (`ls -l`), does the interpreter it names exist, is the config file where the app expects it, can the image's user read it, what does `env` show. Two details matter. First, **`--entrypoint` also clears the image's `CMD`**, so what you get is exactly the command you named plus any arguments you append; this is why `--entrypoint sh` gives an interactive shell rather than running the app's default arguments through `sh`. Second, `-it` is required for an interactive shell, and adding `--user root` temporarily lets you look at files the image's own unprivileged user cannot read — useful for confirming a permissions theory. Add the same runtime context the real container had — the same `-e` variables, the same mounts, the same `--user` — or you will "prove" the image is fine while the failing configuration is untested. A frequent outcome of this exercise is discovering that a mount shadows the directory the binary lives in, which the image alone would never reveal. ## The routing habit Silence should trigger a decision tree, not a shrug: `docker ps -a` for status, `.State.Error` and `.Path`/`.Args` for what the engine tried, `.HostConfig.LogConfig.Type` for whether reading is even possible, `docker cp` for files the app wrote itself, and `--entrypoint sh` for the image's own contents. Each answer forecloses a class of cause, which is what turns a twenty-minute stare into a three-minute diagnosis.
- Why does `--entrypoint sh` give you a shell instead of running the image's default arguments?Because `--entrypoint` clears the image's `CMD` as well as replacing its `ENTRYPOINT`. The container therefore runs exactly `sh` plus whatever arguments you append on the command line, so with `-it` you get an interactive shell. If you want the shell to run something specific, append it: `--entrypoint sh <image> -c 'ls -l /app'`.
- When does `docker logs` return an error instead of empty output, and what does that tell you?When the container's logging driver ships output off the host and cannot be read back locally, `docker logs` reports that the configured logging driver does not support reading. That is a statement about the driver, not the application: check `.HostConfig.LogConfig.Type`, then go and read the logs wherever that driver sends them. Modern engines keep a local cache that usually avoids this, but it can be disabled.
- The application writes its crash detail to a file inside the container. How do you retrieve it after the container has exited?`docker cp <container>:/path/to/file .` works on stopped containers, because the writable layer survives until the container is removed. For the whole filesystem, `docker export <container> > fs.tar`. The durable fix is to make the application log to stdout so the daemon captures it — file-only logging turns every post-mortem into an archaeology exercise.
- You want to reproduce the failure interactively. What must you copy from the original run?Everything that shaped the environment: the same `-e` variables, the same volume and bind mounts, the same `--user`, the same working directory and network mode. An `--entrypoint sh` run with none of those tests a different situation and often 'proves' the image is fine. Mounts especially matter, because a mount can shadow the very directory the binary or config lives in.
saying these in an interview costs you the question
- Concludes the container never ran just because logs are empty
- Believes docker captures output written to files inside the container
- Thinks docker cp requires a running container
- Forgets that --entrypoint also clears the image CMD
- Runs --entrypoint sh without the original mounts or environment
- Blames the application when the logging driver simply cannot be read