How to SSH into a Docker container
Whether you’re new to DevOps or have years of experience, you’re probably familiar with Docker, which is one of the most popular and reliable solutions for containerizing your applications. In this article, we’ll discuss how to SSH into Docker containers to read logs, debug errors, or just make sure they’re operating as expected.
Docker containers are self-contained environments that have all the files and dependencies required for an application to function. Containers are isolated from each other and provide a fresh slate to work with, though containers can also be set up to talk to each other. When you inspect or connect to a Docker container’s shell, you can debug logs, ensure the container is running as expected, execute arbitrary commands, and verify it’s reachable by other containers. This is easy to accomplish if you’re trying to inspect a container that’s running on your local machine, but becomes more cumbersome if you want to debug a remote container.
In this article, you’ll learn why and how you can access the shell for local containers. You’ll also learn how you can use Tailscale SSH to access a remote Docker container without the complexity of key management or bastions.
Why you need to inspect your containers
As a developer, you need to be able to inspect your containers to ensure that they’re running as expected and to debug any issues that might arise. This is a vital part of developing with Docker.
Following are the top three reasons you might want to have shell access to your Docker containers.
Debug logs
Logs are one of the most important data points needed when troubleshooting an application. Opening a shell into a container and fetching the application system logs helps you locate an instance where the application was faulty, or ensures that it was running as expected.
Execute commands
You may want to execute commands. For example, you may want to install dependencies or run custom code in your development environment — whether in virtual environments such as GitHub Codespaces, or in your local container — to keep the commands isolated from your primary environment.
Verify the container is reachable by other containers
Testing connectivity between containers in a network is important because it helps you make sure your services are interacting as required. For example, if you want to make sure your database is talking to your backend service when there’s an error, you can test the connectivity by accessing the container directly and then pinging other containers.
Opening a shell into a container
Now that you know why you would want to login to a Docker container, let’s take a look at how you can actually do it. There are a few different approaches, but the most common is to use the docker exec
command, which doesn’t require you to have an entry point.
The docker exec
command lets you execute any commands you need to run inside the container, verify that the container is reachable by other containers, or do any of the other actions mentioned previously.
In order to use docker exec
, you need to find the ID or the name of the container you want to inspect.
The docker ps
or docker container ls
command will list all the running containers, and you can obtain the ID or name of the container you wish to inspect:
hrittik@tail2:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4277ba8ceb27 memcached "docker-entrypoint.s…" 38 seconds ago Up 37 seconds 11211/tcp mystifying_gauss
add801357cf7 neo4j "tini -g -- /startup…" 41 seconds ago Up 40 seconds 7473-7474/tcp, 7687/tcp lucid_rhodes
caac7023ec74 nginx "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp gallant_nobel
ada16dd695e5 mongo "docker-entrypoint.s…" About a minute ago Up About a minute 27017/tcp vibrant_einstein
e6166f6bdb33 redis "docker-entrypoint.s…" 3 minutes ago
Once you’ve obtained the name or ID, you can use the docker exec
command to run whatever commands you may want to:
docker exec -it <container_id> /bin/bash
You can read more about what exec is doing in the official Docker documentation — but in short, you’re telling Docker that you would like to open an interactive shell on <container_id>
and run /bin/bash
, which will open a bash session.
You can then use the session to install or access whatever you need to.
The specific commands you need to give to the Docker container will vary depending on the operating system that the Docker container is running.
SSH into a Docker container using Tailscale
docker exec
is very powerful for working locally, but what if you want to access your containers remotely? You could set up your containers to accept SSH connections, but then you need to worry about managing SSH keys, rotating credentials, and the usual headaches of running an SSH server.
Tailscale simplifies the entire process, allowing you to establish SSH connections between devices in your tailnet, as authorized by your access controls, without managing SSH keys. Tailscale authenticates your SSH connection using WireGuard.
Tailscale is a zero-config VPN that you can install on your devices to provide secure access to your resources. This means you can also install it on a Docker container, and Tailscale makes it easy to access your host and your containers over the network. You can even transfer files, such as your logs, between machines — all through a decentralized, open-source WireGuard protocol via point-to-point connections.
Install Tailscale
Tailscale supports multiple operating systems, and on Linux, the process is streamlined to run and install the software with one command on a host.
Before you begin, make sure you have curl
installed. If you don’t, run the following command to add curl
to any Debian container:
apt-get update; apt-get install curl -y
After curl
is installed, install Tailscale using this command:
curl -fsSL https://tailscale.com/install.sh | sh
Tailscale supports Docker containers on a range of operating systems besides Linux:
You can learn more about other installation methods on Tailscale’s download page.
Run and authenticate
Start the Tailscale daemon in userspace networking mode:
tailscaled --tun=userspace-networking --socks5-server=localhost:1055 &
This will connect to your network, but not act as a network device for your computer.
After your instance is installed on your server, you need to run it using tailscale up
. As this is your first startup, you will receive the following prompt:
$ tailscale up
To authenticate, visit:
https://login.tailscale.com/a/ff83ba3909f6
You will receive a prompt to authenticate at a specific URL; visit the URL to authenticate your device.
Setup SSH server using Tailscale
Once you’ve logged in to Tailscale, you should be able to see your device (in this case, a Docker machine) in the admin console:
The portal lists every machine that has ever been registered under your account, even those that aren’t connected or where Tailscale needs to be updated. Additionally, this is where you can find your devices’ IP addresses and whether they are running an SSH server.
To set up your Docker container to act as a Tailscale SSH server, restart Tailscale in the container with the -ssh
flag enabled:
tailscale up --ssh
Connect to the SSH server using Tailscale
Now you can connect to the SSH server using Tailscale SSH, without the need to configure authorization keys. To begin, use tailscale ip
to find the Tailscale IP for the SSH server in your Docker container:
hrittik@tail2:~$ tailscale ip
100.95.96.66
fd7a:115c:a1e0:ab12:4843:cd96:625f:6042
If your account name is “username” and your Tailscale IP address for the Docker container is “100.95.96.66”, you can SSH into the container from any other device on the same Tailscale network with the following command:
ssh username@100.95.96.66
Note, however, that using SSH to connect machines owned by different users on the same may require changes to your ACLs.
Introducing MagicDNS
If you need to, you can find the Tailscale IPs for your other devices on the admin console; however, remembering all the IPs needed to do so can be tedious and time-consuming. This is where MagicDNS comes in.
MagicDNS is a feature provided by Tailscale that makes it easier for you to connect to other devices on your tailnet by automatically registering a human-readable DNS name for each device. When you have MagicDNS enabled, you can reach a device using its shortname instead of its Tailscale IP address.
To enable MagicDNS, navigate to the DNS page in the admin console, enter a DNS provider or choose from one of the pre-existing presets, and click Enable MagicDNS:
Now you can access Docker containers without caring about any underlying access infrastructure, keys, or networks.
If desired, you can configure the SSH server with an ACL tag, which provides more granular control, like key exchange duration and access controls.
Wrapping Up
As a developer, you need to be able to inspect your containers to ensure that they’re running as expected and to debug any issues that might arise — this is a vital part of developing with Docker.
In this article, you’ve learned how Tailscale can help you easily SSH into servers. If you have more than one server, the features that Tailscale offers — like providing secure, lower-latency connections with automatic key verification, file transfers, and the ability to switch networks without losing your work — are incredibly helpful while SSHing.