You started a container in detached mode, ran docker attach to peek at it, and now you can't get back to your shell without taking the container down with you. Ctrl+C kills the process. If the main process is a shell, exit does the same thing.
There's an escape hatch, but it has a handful of preconditions that quietly make it not work. Here's how detach actually behaves, and why docker exec is the better starting point for almost everything else.
Detach with Ctrl+P, Ctrl+Q
The detach escape sequence is Ctrl+P followed by Ctrl+Q. It tells the Docker CLI to disconnect your terminal's streams from the container while leaving the process running inside. The sequence only works if the container was started with both -i (interactive) and -t (TTY) flags.
Start a container in detached mode with both flags set:
1docker run -dit --name topdemo alpine top -b
Attach to it:
1docker attach topdemo
top snapshots stream into your terminal:
12345Mem: 3128492K used, 4906248K free, 1872K shrd, 48312K buff, 1739220K cachedCPU: 1% usr 0% sys 0% nic 98% idle 0% io 0% irq 0% sirqLoad average: 0.08 0.04 0.02 1/412 6PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND1 0 root R 1700 0% 1 0% top -b
Now press Ctrl+P then Ctrl+Q. The Docker CLI intercepts the sequence, prints read escape sequence, and drops you back at your host shell. The container keeps running:
1docker ps --filter name=topdemo
12CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa7c14b9d3f02 alpine "top -b" 15 seconds ago Up 14 seconds topdemo
If you started the container with plain docker run -d and no -i -t, the same sequence does nothing. Signals get forwarded straight to the container process, so ^P^Q is handed to the application inside, which almost certainly ignores it. The only way out of that attach session is Ctrl+C, which terminates the container.
Why attach shares your terminal streams
docker attach wires your terminal's stdin, stdout, and stderr to the streams of the container's PID 1, the process started by the image's ENTRYPOINT or CMD. It doesn't spawn a new process. Anything you type goes into that process's stdin, and anything it writes shows up on your terminal. You're sharing a session with the container's main process, not opening a new one.
That's also why everything you type while attached affects the primary process. With --sig-proxy on (the default), Ctrl+C sends SIGINT to PID 1. If PID 1 happens to be a shell, typing exit ends that shell, and the container along with it. The container's lifecycle is bound to whatever PID 1 is doing.
One more thing the streams model implies: multiple docker attach sessions on the same container all see the same stream. Two terminals attached to the same container will both see the output and both feed input into the same process. Don't rely on this for collaborative work, but it explains the behavior if it surprises you.
When to use attach versus exec
docker exec starts a new process inside an existing container. docker attach connects to the existing primary process. They're not interchangeable, and docker exec is what you want most of the time.
Use docker exec to open a shell, run a quick diagnostic, or invoke a database client that ships inside the image without installing one on your host. For a shell in a running NGINX container:
1docker exec -it nginx-test /bin/sh
docker attach earns its place when you genuinely need to interact with the main process. Containers running an interactive REPL (read-eval-print loop) that you started with docker run -it are the obvious case. So is an application that prints live status to stdout while also taking input. And when PID 1 is hung and you want to see what it's actually doing on the terminal it owns, attach is the only way in.
For pure log-following without interaction, docker logs -f is the right tool. It reads from the same source without tying up the process's stdin, and you can disconnect at any time without affecting the container. See how to view Docker container logs for the full set of flags.
Common pitfalls
Ctrl+P collides with shell history. In most shells and Emacs-style readline editors, Ctrl+P moves to the previous command in history. Inside an attached session Docker buffers that keystroke waiting for Ctrl+Q, which makes Ctrl+P feel broken for history navigation. If you attach to containers regularly and lean on Ctrl+P in your shell, override the detach sequence per-container with --detach-keys:
1docker attach --detach-keys="ctrl-x,ctrl-q" topdemo
Or set a global default in ~/.docker/config.json:
123{"detachKeys": "ctrl-x,ctrl-q"}
Detach doesn't work without -i -t. When the container was started with plain docker run -d, the detach sequence is forwarded to the application as ordinary input. There's no detachable terminal session for Docker to release, so the only way out of docker attach is Ctrl+C, which kills the container. If there's any chance you'll want to attach later, start the container with -dit instead of just -d. See how to start a Docker container for more on those docker run flags.
PID 1 ignores SIGINT and SIGTERM by default. This one is genuinely surprising the first time it bites you. The Linux kernel treats PID 1 specially and discards any signal with the default action, so if your container's entrypoint is something like python app.py with no signal handlers registered, Ctrl+C while attached won't terminate it cleanly. The container keeps running until you docker kill it. Production images that need a clean shutdown should run a proper init like tini, or handle SIGTERM and SIGINT in code.
There's a 1MB output buffer. While a client is attached, Docker buffers about 1MB of stdout. Once that buffer fills, the application's writes block on Docker. For high-throughput, log-heavy containers, a slow attach session can therefore slow down the application itself. Use docker logs when you're inspecting a chatty container and you care about its throughput.
You can't redirect stdin to a TTY-attached container. Piping a file into docker attach looks reasonable but Docker refuses it when the container was started with -i -t. The TTY layer doesn't pass through cleanly. If you need to feed data into a container's stdin programmatically, either start it without -t or use docker exec -i.
Final thoughts
docker attach is a narrow tool for a specific case: you need to talk to the primary process of a container, and only that process. For everything else, docker exec is the right starting point, and the FAQ on running commands in a Docker container walks through it in detail.
Attaching and detaching is fine for ad-hoc debugging. But if you find yourself routinely attaching to containers to read live output or check what they're doing, that's SSH-into-a-VM thinking applied to a container fleet, and it stops scaling once you have more than a handful of services running.
Dash0's infrastructure monitoring collects container logs and distributed traces over OpenTelemetry, so you can see what every container is doing without attaching to any of them.
Start a free trial to see container output, resource metrics, and traces in one view. No credit card required.