PQ
PQ.Hosting

Currency

How to Connect to a Docker Container: exec, attach, logs, and Debugging Without a Shell

Author
PQ
March 04, 2026
5 min read
803 views
How to Connect to a Docker Container: exec, attach, logs, and Debugging Without a Shell

The container is running but something is wrong. Logs do not give the full picture, a config needs to be checked from inside, a process is hanging for unknown reasons. To investigate, you need to get in — and Docker provides several fundamentally different tools for this. The right choice depends on whether the container is currently running, whether it has a shell, and what exactly needs to be done.

Method 1: docker exec — The Right Way

docker exec launches a new process inside an already-running container. This is the primary tool for interactive access and one-off commands.

Open an interactive bash shell:

docker exec -it container_name bash

If bash is not available — try sh:

docker exec -it container_name sh

Alpine-based images and many minimal containers do not include bash — only sh. Attempting bash in such a container gives: OCI runtime exec failed: executable file not found.

Flags: -i keeps stdin open (interactive mode), -t allocates a pseudo-TTY. Together -it gives a full interactive session with colors, autocomplete, and control characters.

Run a single command without entering a shell:

docker exec container_name cat /etc/nginx/nginx.conf
docker exec container_name ps aux
docker exec container_name env

Run as a specific user:

docker exec -it -u root container_name bash
docker exec -it -u www-data container_name sh

Useful when the container runs as an unprivileged user but root access is needed for debugging.

Set an environment variable for the command:

docker exec -e DEBUG=true container_name python manage.py check

Method 2: docker attach — Connect to the Main Process

docker attach connects your terminal to the container's main process (PID 1), not a new one. This is the key difference from exec.

docker attach container_name

Used when real-time output of the main process is needed or input must be sent to it. Important limitation: pressing Ctrl+C sends SIGINT to the main process and may stop the container.

To detach without stopping the container — use the escape sequence Ctrl+P followed by Ctrl+Q. This only works if the container was started with -it.

Method 3: docker run -it — Enter on Start

If the container is not yet running and access is needed immediately on start:

docker run -it ubuntu:22.04 bash

For image debugging — override the entrypoint:

docker run -it --entrypoint bash nginx:latest

This ignores the ENTRYPOINT from the Dockerfile and launches bash instead. Useful when examining an image before the application starts, or when the service crashes immediately and exec cannot keep up.

Method 4: docker cp — Copy Files Without a Shell

When only a file needs to be retrieved — no interactive session required. docker cp works without a shell and even with stopped containers:

Copy from container to host:

docker cp container_name:/etc/nginx/nginx.conf ./nginx.conf

Copy from host to container:

docker cp ./new_config.conf container_name:/etc/nginx/nginx.conf

After replacing a config file, reload the process inside the container:

docker exec container_name nginx -s reload

Method 5: docker logs — Read Output Without Entering

Before entering a container, check the logs — the answer is often already there:

docker logs container_name

Follow in real time:

docker logs -f container_name

Last 100 lines:

docker logs --tail 100 container_name

With timestamps:

docker logs -t container_name

Last 30 minutes:

docker logs --since 30m container_name

Find the Container: Name, ID, and Status

Running containers:

docker ps

All containers including stopped:

docker ps -a

IDs only (useful in scripts):

docker ps -q

Both full name and the first characters of the ID can be used:

docker exec -it a3f bash

Connecting to a Container Without a Shell: distroless and scratch

Minimalist images based on distroless (Google) or scratch contain no bash, no sh, no basic utilities at all. docker exec ... bash will give an error. This is intentional — smaller attack surface.

Option 1 — nsenter via the host:

Get the PID of the container main process:

docker inspect --format '{{.State.Pid}}' container_name

Enter the container namespaces:

sudo nsenter -t <PID> -m -u -i -n -p -- sh

nsenter enters the container namespaces directly at the kernel level, bypassing the Docker API. Works even if the container has no executable files at all.

Option 2 — ephemeral debug container (Docker 20.10+):

docker debug container_name

Attaches a temporary debug container with a full toolset to the target container, sharing its namespaces.

Option 3 — install utilities into the running container:

docker exec -it container_name apt-get install -y procps curl
docker exec -it container_name sh

Changes do not survive container recreation, but they are sufficient for debugging.

Useful Diagnostic Commands Inside a Container

After entering the container:

Processes:

ps aux

Network connections:

ss -tlnp

Environment variables:

env | sort

Disk usage and distribution info:

df -h
cat /etc/os-release

Quick Reference

Task Command
Enter a running container docker exec -it name bash
Enter via sh (Alpine etc.) docker exec -it name sh
Run one command docker exec name command
Run as root docker exec -it -u root name bash
Connect to main process docker attach name
Detach without stopping Ctrl+P then Ctrl+Q
Enter on container start docker run -it image bash
Override entrypoint docker run -it --entrypoint bash image
Copy file from container docker cp name:/path/file ./file
Follow logs in real time docker logs -f name
Enter distroless container sudo nsenter -t $(docker inspect --format {{.State.Pid}} name) -m -u -i -n -p -- sh
List running containers docker ps

Share this article