Inspect a running Docker container without turning debugging into the fix
Identify the exact replica, preserve logs and inspect data, use docker exec instead of attach, handle shell-less images, and carry the repair back into the image.
A container is serving errors and you need to inspect its files. Installing SSH inside it creates another daemon, another credential path and a misleading mental model. Docker already provides a controlled way to start a diagnostic process in a running container: docker exec. Use it to gather evidence, not to make an undocumented production repair.
Identify the exact container and preserve its context
docker ps --no-trunc --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}'
docker inspect --type container app-web-1 --format 'image={{.Config.Image}} pid={{.State.Pid}} status={{.State.Status}} started={{.State.StartedAt}}'
In a scaled service, a name copied from another host may point to the wrong replica. Record the host, container ID, image digest, start time and incident window. Save logs before a restart rotates or destroys the evidence:
mkdir -p incident-2026-07-27
docker logs --timestamps --since 30m app-web-1 >incident-2026-07-27/container.log 2>&1
docker inspect app-web-1 >incident-2026-07-27/inspect.json
Use exec, not attach, for a new shell process
docker exec -it app-web-1 sh
docker exec starts a new executable while the container’s primary process is running. -i keeps standard input open; -t allocates a pseudo-terminal. By contrast, docker attach connects your terminal to the existing PID 1 streams. Attaching to a web server does not create a shell and careless keystrokes can send signals or input to the application.
Prefer targeted, non-interactive evidence commands
docker exec app-web-1 ps
docker exec app-web-1 cat /etc/os-release
docker exec app-web-1 sh -c 'id; pwd; ls -la /app; df -h /app'
docker exec app-web-1 sh -c 'ss -lntp 2>/dev/null || netstat -lntp 2>/dev/null'
Targeted commands are reproducible and easy to record. Docker requires the command to be an executable: shell operators such as &&, pipes and redirections need an explicit shell, for example sh -c 'first && second'. Quoting the whole chain as the executable will fail.
Match the application user before assuming root
docker inspect app-web-1 --format 'configured-user={{json .Config.User}}'
docker exec --user 10001:10001 --workdir /app app-web-1 id
docker exec --user 10001:10001 --workdir /app app-web-1 ./healthcheck
Running diagnostics as root can hide the permission failure experienced by the service. Start as the configured runtime identity. Elevate only for a named read-only check and record it. Avoid --privileged: Docker documents that privileged containers receive broad host-facing capabilities and are not securely sandboxed.
Do not expect Bash—or any shell—to exist
docker exec -it app-web-1 /bin/bash
# If Bash is absent:
docker exec -it app-web-1 /bin/sh
# If every shell is absent, run a known application binary:
docker exec app-web-1 /app/server --version
Distroless, scratch and hardened images may intentionally contain no shell, package manager, ps or network utilities. “Executable file not found” does not mean the container is unreachable. It means the requested tool is absent from that filesystem.
Use Docker Debug for a shell-less image where available
docker debug app-web-1
Docker documents docker debug as an alternative that can provide a toolbox shell even when the target is slim or shell-less. Availability and licensing can vary by installation, so confirm it in your environment and audit its use. The toolbox is for observation; do not confuse tools visible in the debug environment with files shipped in the application image.
Copy evidence out without editing the container
docker cp app-web-1:/app/config/runtime.json incident-2026-07-27/runtime.json
docker diff app-web-1 >incident-2026-07-27/filesystem-diff.txt
sha256sum incident-2026-07-27/runtime.json
Inspect copied material according to its sensitivity—runtime files may contain credentials or customer data. docker diff shows filesystem changes relative to the image, which can reveal an unexpected write path, but it does not explain volumes and bind mounts by itself.
Inspect mounts before blaming the image
docker inspect app-web-1 --format '{{range .Mounts}}{{println .Type .Source "->" .Destination "rw=" .RW}}{{end}}'
docker exec app-web-1 sh -c 'mount 2>/dev/null; df -h 2>/dev/null'
A file visible at /app/config may come from a bind mount, named volume, secret or configuration object. Editing it inside the container might mutate shared external state. Resolve the mount source and ownership before touching anything.
Understand environment inspection limits
docker inspect app-web-1 --format '{{range .Config.Env}}{{println .}}{{end}}'
docker exec app-web-1 env
Both commands can expose secrets to the terminal, shell history or incident bundle. Prefer listing variable names or querying a specific non-secret value. Docker notes that environment supplied to an exec process applies only to that process; it does not reconfigure PID 1.
Know why exec sometimes fails
| Symptom | Meaning | Next check |
|---|---|---|
| Container is not running | Exec requires a live PID 1 | Inspect state, exit code, OOM flag and logs |
| Container is paused | Docker rejects exec into paused state | Confirm why it was paused before unpausing |
| Executable not found | Tool or shell is absent, or PATH differs | Use an absolute path or approved debug toolbox |
| Permission denied | Runtime user or filesystem policy blocks it | Reproduce as configured UID; inspect modes and LSM events |
| Exec dies on restart | It exists only while the original PID 1 runs | Capture evidence outside the container |
Make the actual repair in source and rebuild
git switch -c fix/container-config-validation
docker build --pull --tag app:test .
docker run --rm app:test ./healthcheck
docker image inspect app:test --format '{{.Id}}'
A package installed or file edited through exec disappears when the container is replaced and cannot be reviewed from the Dockerfile. Reproduce the fault in a controlled environment, change source or image construction, test the health boundary, publish an immutable image, and roll it out through the normal deployment path.
Verify replacement and preserve the handover
docker ps --filter name=app-web --format '{{.ID}} {{.Image}} {{.Status}}'
curl --fail --show-error --max-time 5 https://service.example.net/health
docker logs --timestamps --since 5m app-web-1
Confirm the user-facing service, not merely the presence of a shell or a green container status. Record the old and new image digests, evidence collected, commands run, change reviewed and rollback decision.
A shell inside a container is an observation point, not a configuration-management system. Diagnose the running instance, then make the durable repair in the image and deployment definition.
Sources: the solved Stack Overflow question (question by Andrew; accepted answer by larsks, CC BY-SA), Docker’s official container exec, container attach, and Docker Debug documentation.
Primary source: Review the official reference ↗