1. Overview

Docker is a popular containerization platform. Through its docker command-line tool, it offers various subcommands that greatly simplify the management of containers on the system. Among these subcommands, exec and attach are two commands generally used for interacting with a running container.

Despite their seemingly similar functionality, the docker exec and docker attach commands are very different in their interactions with the running container. To understand the differences, it’s important to learn how these two commands work under the hood.

In this tutorial, we’ll learn what the docker attach and docker exec commands do and their differences.

2. Docker Exec

The docker exec command executes any arbitrary command in a running container. Specifically, the docker exec command runs a given command in the default working directory of the container under a new process. Generally, the command has the following expression:

docker exec [OPTIONS] CONTAINER COMMAND

Importantly, the COMMAND must be an executable and not a quoted script. This is because the Docker engine passes the COMMAND to the exec syscall under the hood.

For example, a common mistake is to pass a one-liner shell script as the COMMAND:

$ docker exec ubuntu "echo hello && echo world"
OCI runtime exec failed: exec failed: unable to start container process: exec: "echo hello && echo world": executable file not found in $PATH: unknown

The problem is that a one-liner script is not an executable. To run a shell script using docker exec, we must specify the executable, sh, and pass the script as arguments to the sh command. Therefore, the correct way to run a one-liner shell script is to prefix them with the executable sh -c:

$ docker exec ubuntu sh -c "echo hello && echo world"
hello
world

Besides that, the docker exec offers various options to use with the command. Importantly, the -i and -t options are critical for keeping the STDIN stream open and allocating a pseudo-tty to allow use cases such as starting an interactive shell in the container.

3. Docker Attach

The docker attach command attaches the current terminal standard input, output, and error streams to the primary process of a running container. With the streams attached, we can interact with the process inside the container as if the process is running on our terminal.

Before we look further, let’s look at the meaning of primary process in the context of a Docker container.

3.1. Primary Process

Within a running Docker container, there’ll be a regular process running with a PID of 1. This process with a PID of 1 is known as the primary process. The primary process is the process that is started by the command specified in the ENTRYPOINT or CMD in the Docker image. When we run the docker attach command on a container, the command attaches to the primary process of the container.

3.2. The Command

The docker attach command takes a list of options and the container name:

docker attach [OPTIONS] CONTAINER

The CONTAINER argument must be the name or ID of a currently running Docker container. Besides that, the command supports options like –no-stdin to explicitly not attach the standard input stream. Additionally, we can prevent signals from being sent to the process by specifying the –sig-proxy false option.

3.3. Demonstration

We use the redis-cli command for demonstration as it’s a process that listens to the standard input stream and prints to the output stream. This allows us to demonstrate the attachment of both the standard input and output streams.

Firstly, we’ll start a detached Docker container using the redis Docker image. Importantly, we’ll need to run the redis-cli command to start it as the primary process of our container:

$ docker run -dit --name redis --rm redis redis-cli
2d3d508df8ad7b7e9c3864ae9733b1bd32e12f94330559f1ee6a4f753c299b9e

Critically, we’ve specified the -it option to keep the input stream of the container opened. Additionally, we start the container in detached mode using the -d option.

Then, we can attach to the redis-cli process using the docker attach command:

$ docker attach redis
not connected>

As expected, the redis-cli cannot connect to the Redis server because we’ve overwritten the default CMD that starts the Redis server. But this won’t affect our demonstration here as we’re only interested in seeing that the input and output stream of our terminal and the primary processes are linked.

Let’s issue the SET command through the standard input stream:

not connected> SET user bob
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected>

As we can see, the redis-cli process reads the commands we’ve put on the standard input stream and acts accordingly. Additionally, any output or errors the process put onto the standard output and error streams show up at our terminal due to the attachment.

4. Difference Between Docker Exec and Docker Attach

The primary difference between the docker exec and docker attach command is that the docker exec executes a command in a new process. It’s typically used for starting an interactive shell in a running container, or to execute arbitrary commands in the container environment.

On the other hand, the docker attach command does not start any new process. It attaches the current terminal’s standard input and output streams to the primary process of the running containers. The primary usage of the docker attach command is when we want to send commands to the primary process directly.

5. Conclusion

In this tutorial, we’ve learned that the docker exec command and the docker attach command are very different commands for interacting with the process in a running container.

Specifically, the docker exec command executes commands in a new process in the container. However, the docker attach command attaches the standard IO streams of our terminal to the primary process of the running container.