An Introductory How-To, With Examples, of Docker Exec

6 min read

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.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.