Troubleshooting Docker workloads often requires you to run a command in the container. Or run multiple commands in a Docker container. For example you may need to run a command and pipe the output to another command for processing. Or you might want to run multiple commands sequentially. How do you do that? Well luckily it is not too hard. But there are some things to be aware of.
How to run a command in a Docker container
Most docker containers are light weight by design and only run a single service. As a result you can’t just SSH into the container like you can with a server or VM. So how do you run a command in a Docker container? With the
docker exec command. Note that we will start all
docker commands with the
sudo command. Running docker commands requires read/write access to the docker daemon’s socket, you can use sudo or add your user to the docker group.
For example to run the directory listing command
ls inside a running container you can do the following:
sudo docker exec $container ls
$container is the id of name of the container. For example if my container name is
epic_driscoll I can run:
sudo docker exec epic_driscoll ls
Using a container with the id of
sudo docker exec 2a5e32051da8 ls
On my docker host I have a single docker container running. Here is the output of
docker ps which shows the running containers:
ubuntu@dev-linuxhit-com:/home/ubuntu# sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a5e32051da8 nginx "/docker-entrypoint.…" 4 hours ago Up 4 hours 80/tcp epic_driscoll
The container ID is 2a5e32051da8 and the image running is a stock NGINX image from Dockerhub. When I run
docker exec on this host I get the following output show the contents of the containers root directory:
root@dev-linuxhit-com:/home/ubuntu# docker exec 2a5e32051da8 ls
Run multiple commands in a Docker container
How about running multiple commands? For example, what if I want a directory listing and to create a new file using the
touch command? I can run two docker execs,
docker exec $container ls and
docker exec $container touch newfile.txt. But there is a more efficient way to do it. We can run both commands in a single
docker exec by spawning a shell in the docker container and passing a string with our commands.
sudo docker exec -it $container bash -c "ls -l; touch newfile.txt"
Notice we added quite a bit to this command. Let’s dive into that. First we added
-it, these two flags tell docker to take STDIN from our terminal and also allocate a pseudo TTY. Essentially this makes command session behave like a terminal. After the
$container we changed to the command to run a bash shell and pass our commands in via bash’s -c flag:
bash -c "ls -l; touch newfile.txt". And we encapsulate our commands in quotes and put a semicolon in between them. Putting them quotes prevents them from getting interpreted by our current shell and the semi-colon tells the container that we are starting a new command.
Important note: In a minimal linux container you might not have the bash shell installed. If that is the case you try replacing bash with
sudo docker exec -it $container /bin/bash -c "ls -l; touch newfile.txt"
To prove our touch command works let’s add a third command. Another
ls so we can see if our file was created:
ubuntu@dev-linuxhit-com:~$ sudo docker exec -it 2a5e32051da8 bash -c "ls; touch newfile.txt; ls "
bin docker-entrypoint.d home media proc sbin tmp
boot docker-entrypoint.sh lib mnt root srv usr
dev etc lib64 opt run sys var
bin docker-entrypoint.d home media opt run sys var
boot docker-entrypoint.sh lib mnt proc sbin tmp
dev etc lib64 newfile.txt root srv usr
Our file was created. Bravo!
You are now ready to string together commands and run them in your container. As I mentioned earlier, running commands in containers is great for troubleshooting. However its important to design and build your containers with the idea that you never login to them. Containers are best when ephemeral, easy destroyed and recreated.