Introduction
Docker exec is a command that allows the execution of any given command within a Docker container.
This means it will interpret the arguments passed to it as commands to be run inside the container.
Let’s look at a quick example for clarity. The command docker exec CONTAINER ls
will execute the command ls
inside the container tagged CONTAINER
.
Looks simple, right? Well, there are a few things to consider, but in general, yes, it is simple. And powerful.
Follow me for a detailed discussion of what docker exec is and how to make the most of it.
What Is Docker Exec?
All right. We covered the basics, but what exactly is docker exec?
As you may already know, the way Docker runs is by having a daemon expecting commands to be sent to it, like any other server software (think MySQL, for instance).
The Docker command is a program that can communicate with this daemon and have it run tasks such as build images from Dockerfiles or start and stop containers, among others.
In particular, docker exec is the best way to run commands inside the container. There are several ways to use this command, depending on your specific needs.
Let’s explore the more common scenarios.
How to Use Docker Exec to Run Commands in the Background
Let’s say you want to have a program run in the background, a server of your own creation, for instance.
In that case, the best way to go would be to use docker exec’s detached mode, like this:
docker exec
-d
CONTAINER command
This command will ask the Docker engine to execute the command command inside the container and keep it running while you move on to another task.
Since the command is still running in the background, there will be no problem if another process needs to interact with it.
The -d stands for detached, but you can also think of it as the d for daemon.
How to Pass Environment Variables to Docker Exec
The commands run with docker exec do not inherit the environment where the Docker engine runs. As annoying as this may seem at first, it makes perfect sense since the whole idea of Docker containers is to run in complete isolation.
If you want to establish the value of environment variables at runtime, you have the option of using another modifier: -e
(or --env
in its long form):
docker exec -e MY_VAR=VALUE CONTAINER command
In this case, the command will be able to read the variable MY_VAR
from the environment and get the value VALUE
.
One typical use of this is to forward environment variables from the host to the container, like this:
docker exec -e MY_VAR=$ENV_VAR CONTAINER command
This way, the value of MY_VAR
is taken from the value of ENV_VAR
at the host.
If there are many definitions to be used constantly, it would probably make sense to store them in a .env
file, such as:
MY_VAR=VALUE OTHER_VAR=OTHER_VALUE YET_ANOTHER_VAR=YET_ANOTHER_VALUE
Should this be the case, you can have Docker inject all the environment variables defined within the file by using the --env-file modifier, like this:
docker exec --env-file=vars.env CONTAINER command
In general, it makes sense to resort to environment variables to store sensitive information.
How to Run an Interactive Command Using Docker Exec
Any command can be run inside a Docker container, including commands that need interactivity (perhaps having the user provide some extra information during execution).
In this case, it makes a lot of sense to use a combination of the -i
and -t
modifiers: -i
(interactive) keeps stdin open and -t
(tty) allocates a pseudo-terminal.
In practice, using both of them combined allows commands to be executed as if they were run outside the container, at the host level.
This can be better explained with an example that doesn’t use -i
or -t
.
If you run a command like docker exec CONTAINER bash
on a running container, you’ll immediately be given back control, which is basically the same as not having run any command at all!
If you want to run a command such as bash
, it’s because you want to have it execute some further commands within the container. For that, you need to be able to interact with it during its execution.
In order to have bash accept user input, you need to keep the standard input open—hence the usage of -i
.
The -t
modifier creates a whole environment where commands are run, similar to what happens when you open a new terminal window. But if you were to run it without the -i
, you’ll get an extremely frustrating experience. You’ll see the terminal; you’ll be able to type (and see the text you typed)—but the container won’t have access to what you just typed. Its standard input won’t be connected, effectively rendering the terminal session useless.
So, a much better way to go about this is to run the command docker exec -it CONTAINER bash
. This will leave you with a fully functional terminal to run commands inside your container.
How to Establish the Working Directory When Running a Command Using Docker Exec
When you run any command using docker exec, the command takes the working directory from the Dockerfile itself.
Sometimes you’ll need to perform a command on a different directory within the container. In this case, you can use the -w
(--workdir
) to specify a different working directory for your command to run at.
For example, let’s say your Dockerfile looks like this:
FROM php:8-fpm RUN apt update \ && apt install -y zlib1g-dev g++ git libicu-dev zip libzip-dev zip \ && docker-php-ext-install intl opcache pdo pdo_mysql \ && pecl install apcu \ && docker-php-ext-enable apcu \ && docker-php-ext-configure zip \ && docker-php-ext-install zip WORKDIR /var/www/symfony_docker RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN curl -sS https://get.symfony.com/cli/installer | bash RUN mv /root/.symfony/bin/symfony /usr/local/bin/symfony
This means that, when you run a command such as docker exec CONTAINER ls
, you’ll get the contents of /var/www/symfony_docker/
as a result.
If you wanted to change that to, for instance, list the contents of /etc/
instead, you could use this command: docker exec -w /etc/ CONTAINER ls
.
This modifier can make a lot of sense in cases where a script depends on being executed in a particular directory.
Conclusion
In this article, you learned the most common use cases for docker exec and what CLI (Command Line Interface) modifiers give you access to each.
Now you can make the most out of one of Docker’s most versatile commands.
This post was written by Mauro Chojrin. Mauro helps PHP developers hone their craft through his trainings, books, workshops and other tools. He's been in the IT industry since 1997 and has held roles such as developer, architect and leader of technical teams. Mauro also likes to write and vlog.