Try

Or browse

    DockerTools

    Connecting to Docker Containers

    Connecting to Docker Containers

    How to connect to a running Docker container and get a shell inside it with docker exec, when docker attach fits better, and what the usual errors mean.

    Suggest editsRSSFollow on Google

    I wrote the first version of this post in August 2014, when the usual way into a running container was a third-party tool called nsenter. I’d been installing SSH on most of the containers I was deploying, which I knew was wrong, and I wanted to get in and have a look around without exposing a port or setting up users.

    That October Docker 1.3 added docker exec (I wrote about installing it at the time), and nsenter stopped being necessary. The post has carried on turning up in searches for things like “docker enter container” ever since, so I’ve rewritten it to describe what you should be typing today. The old method is still at the bottom, for the sake of history.

    The short answer

    If you just want to connect to a running container and get a shell inside it:

    Open a shell in a running container
    docker exec -it my-container bash

    Swap my-container for the name or ID of your container (docker ps will tell you), and swap bash for sh if the image is Alpine or otherwise minimal. Type exit to leave. The container carries on running as though you’d never been there.

    Trying it on a test container

    Start something to poke around in. The official nginx image will do:

    Start a test container
    docker run -d --name web -p 8080:80 nginx
    docker ps

    Now open a shell inside it:

    Enter the container
    docker exec -it web bash

    You’ll land at a root prompt where, by default, the hostname is the container’s ID. That’s a quick way to confirm you’re inside the container rather than on the host.

    The two flags do different jobs. -i keeps standard input open so you can type, and -t allocates a pseudo-terminal so you get a proper prompt and line editing. You want both for an interactive shell. Drop -t when you’re piping data in, and drop both when you’re only running a command.

    Running a single command

    You don’t have to open a shell at all. Leave off -it and put the command straight after the container name:

    Run commands without opening a shell
    docker exec web nginx -v
    docker exec web ls -lha /usr/share/nginx/html
    docker exec web cat /etc/nginx/conf.d/default.conf

    This is what docker-enter testing ls -lha /var/lib/mysql/ did in the original post, minus the extra tool.

    docker exec starts a new process inside the container’s existing namespaces and leaves the main process (PID 1) alone. That means it only works while the container is running, and anything you change from inside lives in the container’s writable layer, so it disappears when the container is removed.

    Useful flags

    A handful of flags come up often:

    FlagWhat it doesExample
    -u / --userRun as a different userdocker exec -u root -it web bash
    -w / --workdirStart in a specific directorydocker exec -w /etc/nginx -it web bash
    -e / --envSet an environment variable for that commanddocker exec -e DEBUG=1 web env
    -d / --detachRun the command in the backgrounddocker exec -d web touch /tmp/hello

    -u root matters more than it looks. Plenty of images switch to an unprivileged user at the end of the Dockerfile, which is sensible until you need to install a debugging tool inside one.

    sh or bash?

    A lot of images don’t ship bash. Alpine-based images have sh (BusyBox’s ash), and distroless or scratch-based images usually have neither. If you try bash on an image without it, you’ll get this:

    What you see when bash isn't installed
    OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH: unknown

    Use sh instead. If that fails too, the image has no shell at all and docker exec can’t conjure one. docker logs and docker cp still work, and for anything more you’ll need to rebuild the image with a shell in it.

    With Docker Compose

    If the container is part of a Compose project, use the service name from your compose file rather than the container name:

    Enter a service with Docker Compose
    docker compose exec web sh

    Compose allocates a terminal by default, so you don’t need -it. If you’re running the command from a script or a CI job, where there is no terminal, add -T to turn that off.

    docker attach is not the same thing

    docker attach connects your terminal to the container’s main process rather than starting a new one. If that process is a shell, you can type into it. If it’s a web server, you’ll watch its output scroll past and not much else.

    The catch is that Ctrl+C is aimed at that main process, and for many servers that stops the container. To leave without stopping it, press Ctrl+P followed by Ctrl+Q. Use docker exec for looking around, and docker attach only when you specifically want the main process’s own input and output.

    Podman and Kubernetes

    Podman accepts the same syntax, so podman exec -it web bash does what you’d expect (I covered getting it running in Running Podman on macOS). In Kubernetes the equivalent is kubectl exec -it my-pod -- sh, with -c to pick a container when the pod has more than one.

    When it goes wrong

    Most failures come down to one of four messages:

    MessageWhat it meansWhat to do
    No such container: webThe name or ID is wrongCheck docker ps -a for the exact name
    ... is not runningThe container has stopped or crashedRead docker logs web, then start it again
    the input device is not a TTYYou used -t from a script, cron or CI jobDrop -t, or use -T with Compose
    "bash": executable file not found in $PATHThe image has no bashUse sh

    If the container keeps exiting before you can get into it, override the entrypoint and start a fresh one from the same image instead:

    Start a shell instead of the normal entrypoint
    docker run --rm -it --entrypoint sh nginx

    That gives you the image’s contents to look at, though not the state of the container that crashed.

    Why not just run SSH?

    It’s what I was doing in 2014, and I’d still talk you out of it. An SSH daemon inside a container is an extra process to supervise and an extra port to expose, all to do something the Docker CLI already does over the Docker socket.

    What nsenter and docker-enter were

    For completeness, here is what this post used to be about. Before docker exec existed, nsenter↗ started a process inside a container’s Linux namespaces, and docker-enter↗ was a small wrapper that looked up the container’s PID for you. Running docker-enter testing dropped you into a shell.

    docker exec does the same job and ships with Docker, which is why I stopped using both. nsenter itself lives on as part of util-linux, if you ever need to enter a container’s namespaces from the host without going through Docker at all.

    Summing up

    Twelve years on, getting into a container takes one line, which is a big improvement on pulling a binary out of an image and copying it into /usr/local/bin by hand. If you’re new to Docker and want the wider tour, that’s the ground I cover in Mastering Docker, Fourth Edition.

    Share

    Related terms: Agent SkillsAI Coding AssistantApplication Programming InterfaceClaude CodeCodexCommand-Line Interface

    Comments