How to SSH into a Docker container

You almost never need to. Run docker exec -it web sh on the machine where the container runs, or ssh -t user@server docker exec -it web sh from elsewhere. A container normally has no SSH server, and adding one is rarely worth it. To control a remote Docker from your laptop, use DOCKER_HOST=ssh://user@server.

Updated . docker exec options, the error messages, the sidecar shell and the sshd Dockerfile were run with Docker on Linux. The exec errors, the helper container and docker cp were checked again with Docker 29.5.

Get a shell: docker exec

docker ps
docker exec -it web sh

-i keeps input open and -t gives you a terminal. Replace web with the name from docker ps. Use bash if the image has it. Useful options:

docker exec -it -u root web sh              # as another user
docker exec -it -w /app web sh              # in another folder
docker exec -e DEBUG=1 web env              # with an extra variable
docker compose exec web sh                  # with Compose: no -it needed

With Podman, the same commands work with podman in place of docker.

On a remote server

SSH into the host, not the container. To do it in one step, ask for a terminal with -t:

ssh -t user@server docker exec -it web sh

Without -t, recent Docker versions stop with cannot attach stdin to a TTY-enabled container because stdin is not a terminal, and older ones with the input device is not a TTY. For more than one command, just log in to the server first.

A short name for the server saves typing here: see SSH config file examples.

Common errors

No shell in the image:

OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH

Try sh. Alpine images have sh and no bash. If sh gives the same error, the image has no shell at all, which is common for minimal images. Start a helper container that shares its process and network view:

docker run --rm -it --pid=container:web --network=container:web alpine sh

From there, ps shows the other container's processes.

Container stopped:

Error response from daemon: container 962d0cc0707e... is not running

You cannot exec into a stopped container. Read why it stopped, and copy files out if you need them. docker cp also works on a stopped container:

docker logs web
docker cp web:/etc/hostname .

Control a remote Docker from your laptop

The Docker client can talk to a remote daemon through SSH, so docker ps and docker exec on your laptop act on the server. No Docker port is opened, and only SSH is used:

DOCKER_HOST=ssh://user@server docker ps
DOCKER_HOST=ssh://user@server docker exec -it web sh

To keep it, create a context:

docker context create --docker host=ssh://user@server remote
docker --context remote ps

You need a working key login to the server, a Docker client on your laptop, and a server user allowed to use the Docker socket. Be aware that access to the Docker daemon is close to root on that machine: Docker's documentation says only trusted users should have it.

Each command opens a new connection, so Docker suggests reusing one in ~/.ssh/config. See SSH multiplexing.

When sshd in a container makes sense

Only when SSH itself is the point:

  • A test server for SSH clients, scripts or Ansible.
  • A tool that can only reach things over SSH or SFTP, and that you cannot change.
  • A jump host or SFTP drop box that you deliberately run as a container.

For getting in to debug something, it adds a service, a key and an open port, and docker exec already does that job. If you do need it, this runs and logs in with a key:

FROM debian:13-slim
RUN apt-get update && apt-get install -y --no-install-recommends openssh-server \
 && rm -f /etc/ssh/ssh_host_* \
 && useradd -m -s /bin/bash app && mkdir -p /run/sshd \
 && install -d -m 700 -o app -g app /home/app/.ssh
COPY authorized_keys /home/app/.ssh/authorized_keys
RUN chown app:app /home/app/.ssh/authorized_keys && chmod 600 /home/app/.ssh/authorized_keys
CMD ["sh", "-c", "ssh-keygen -A && exec /usr/sbin/sshd -D -e -o PasswordAuthentication=no"]
docker build -t sshd-demo .
docker run -d --name sshd-demo -p 127.0.0.1:2222:22 sshd-demo
ssh -p 2222 app@127.0.0.1

Three details matter here:

  • Host keys are created at start, not in the image. Otherwise every container from the image shares the same private host key. The downside is a new key each time a container is recreated, which triggers the host key warning. Keep /etc/ssh in a volume if that matters.
  • The port is bound to 127.0.0.1. Published ports skip ufw. Docker's documentation says traffic is diverted before it reaches ufw's rules, so -p 2222:22 can be open to the world even with ufw on.
  • This image holds no secrets. authorized_keys has only public keys. Never copy a private key into an image.

Questions

Does docker exec need SSH?

No. The docker command talks to the Docker daemon, which starts the process inside the container.

How do I open a shell in a container as root?

Add -u root: docker exec -it -u root web sh. It works even when the image runs as another user.

How do I enter a container on a remote server?

SSH into the server that runs it, then use docker exec. In one step: ssh -t user@server docker exec -it web sh.

Does a shell started with docker exec survive a container restart?

No. It only runs while the container's main process is running, and it is not started again when the container restarts.

Sources

More in SSH on each system

All guides