Docker Exec – How to Run a Command Inside a Docker Image or Container

Docker container ship illustration

Docker has revolutionized how we build, package, and deploy applications. By packaging an app and its dependencies into an isolated container, Docker makes it easy to run that app consistently across different environments. But sometimes you need to peek inside that container and execute commands directly. That‘s where docker exec comes in.

In this article, we‘ll take an in-depth look at docker exec – what it is, how it works, when to use it, and best practices to keep in mind. Whether you‘re a developer troubleshooting an issue or a DevOps engineer performing maintenance on production containers, understanding docker exec is key to effectively working with Docker.

What is docker exec?

Simply put, docker exec is a command that allows you to run a new command inside an already running Docker container. It‘s like SSH‘ing into a remote server to run commands, except in this case the remote server is a Docker container running locally or in the cloud.

The basic syntax looks like this:

docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

For example, to run the ls command inside a container named my-app, you would do:

docker exec my-app ls

This would list the files in the default working directory inside the my-app container.

Some common options you can pass to docker exec include:

  • -i or --interactive – Keep STDIN open even if not attached. This allows you to provide input to the command.
  • -t or --tty – Allocate a pseudo-TTY. This essentially gives you an interactive terminal session inside the container.
  • -w or --workdir – Set the working directory for the command. By default it uses the container‘s default working directory.
  • -e or --env – Set environment variables for the command.
  • -u or --user – Run the command as a particular user.

So a more involved example might look like:

docker exec -it -w /app -e DEBUG=true my-app bash

This would give you an interactive bash shell session inside the my-app container, starting in the /app directory, with the DEBUG environment variable set to "true".

How to use docker exec

Using docker exec is fairly straightforward once you understand the basic syntax. Here‘s a step-by-step example.

Let‘s say we have a Docker container running a Node.js app. We can get the ID or name of the container using docker ps:

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  NAMES
bcd753e2a933   node:14   "docker-entrypoint.sā€¦"   my-app

To start an interactive bash session inside this container, we can run:

docker exec -it my-app bash

This will drop us into a bash shell inside the running container:

root@bcd753e2a933:/app# 

From here, we can run any commands we want. For example, to see the Node.js version:

root@bcd753e2a933:/app# node -v
v14.17.0

Or to see the contents of the /app directory:

root@bcd753e2a933:/app# ls
Dockerfile  index.js  node_modules  package.json

When we‘re done, we can exit the shell with the exit command:

root@bcd753e2a933:/app# exit
exit
$

And we‘re back to our host machine‘s shell.

We can also run commands directly without starting an interactive shell. For example, to see the contents of a file:

docker exec my-app cat /app/package.json

This would output the contents of the package.json file inside the container.

Common use cases for docker exec

So when would you actually want to use docker exec? Here are some common scenarios.

Debugging and troubleshooting

Perhaps the most common use case is debugging and troubleshooting a misbehaving container. If your app is crashing or not working as expected, docker exec allows you to get inside the container and poke around.

You can inspect log files, check environment variables, run diagnostic commands, and generally try to figure out what‘s going wrong. Interactive shell access via docker exec -it is especially useful for exploratory debugging.

Inspecting the container filesystem

docker exec also allows you to inspect the filesystem of a running container. You can list files, view file contents, check permissions, and so on.

This can be useful for checking configuration files, verifying that expected files are present, or investigating disk space issues.

Running maintenance tasks

Sometimes you may need to run one-off maintenance tasks in a production container. For example, you might need to run a database migration, clear a cache, or update a dependency.

docker exec allows you to run these tasks without having to rebuild the container image or start a new container. Just exec in, run the task, and you‘re done.

Automating tests and checks

In a CI/CD pipeline or other automated workflow, you may want to run tests or checks against a freshly built container before deploying it. docker exec allows you to run these automation steps.

For example, you could build a container, start it, exec in to run unit tests, and only proceed with deployment if the tests pass. All of this can be automated in a script or pipeline.

Exec vs other approaches

docker exec is not the only way to run commands in a container. Let‘s compare it to some other common approaches.

docker run

docker run is used to start a new container. You can pass a command to docker run which will be executed inside the container when it starts.

For example:

docker run my-app node -v

This would start a new container from the my-app image and run the command node -v inside it.

The key difference from docker exec is that docker run starts a new container each time, whereas docker exec runs a command inside an existing container.

docker attach

docker attach is used to attach your terminal‘s standard input, output, and error streams to a running container. This allows you to view its ongoing output and also send input to it.

For example:

docker attach my-app

This would attach to the my-app container. Any output from the container would be displayed in your terminal, and any input you type would be sent to the container.

The difference from docker exec is that attach doesn‘t run a new command, it just attaches to the container‘s existing primary process.

nsenter

nsenter is a Linux command that allows you to enter any namespace. In the context of Docker, you can use it to enter a container‘s namespaces, giving you a shell inside the container.

For example:

nsenter --target $(docker inspect --format {{.State.Pid}} my-app) --mount --uts --ipc --net --pid

This would enter the namespaces of the my-app container and give you a shell.

The advantage of nsenter over docker exec is that it works on any Linux system, not just those with Docker installed. However, it‘s more complex to use and requires privileged access to the host system.

Best practices and gotchas

While docker exec is a powerful tool, there are some things to keep in mind when using it.

Be careful with privileged access

When you docker exec into a container, you are running as the root user inside that container by default. This means you have full privileged access to the container‘s filesystem and processes.

While this is often necessary for debugging or maintenance tasks, it also means you can easily make accidental and destructive changes. Always double-check your commands before running them, and consider using a non-root user where possible.

Avoid unintended changes

When using docker exec for debugging or exploration, it‘s easy to accidentally make changes to the container that you didn‘t intend. For example, creating or modifying files, installing packages, or changing configurations.

If you want to ensure your exec session doesn‘t make any permanent changes, you can use the --read-only flag:

docker exec --read-only my-app bash

This starts a bash session but with a read-only filesystem, preventing any changes.

Consider if exec is the right approach

While docker exec is useful in many situations, it‘s not always the best approach. For tasks that modify the container state (e.g. updating a package), it‘s often better to build a new image with the changes and start a new container from that image.

For long-running or complex tasks, it‘s often better to use docker run to start a new container specifically for that task. And for viewing logs or output, consider using docker logs or docker attach instead.

Limitations of docker exec

It‘s also important to understand what docker exec can‘t do.

Firstly, docker exec can only be used on running containers. If a container has exited or hasn‘t started yet, docker exec will fail.

Secondly, docker exec is for running one-off commands, not for starting long-running services or daemons. If you need to start a new background process, it‘s better to use docker run.

Lastly, because docker exec runs commands inside the existing container environment, it may not be suitable for tasks that require a clean or isolated environment. In these cases, starting a new container with docker run is a better choice.

Conclusion

docker exec is a powerful tool that allows you to run commands inside a running Docker container. It‘s useful for debugging, maintenance, inspection, and automation.

To use docker exec, simply specify the container name or ID and the command you want to run. You can use options like -it for an interactive session, -w to set the working directory, and -e to set environment variables.

However, it‘s important to use docker exec responsibly. Be careful with privileged access, avoid unintended changes, and consider if it‘s the right approach for your task. Also keep in mind the limitations – docker exec can‘t start containers, run long-running processes, or provide a clean environment.

By understanding how and when to use docker exec, you can more effectively manage and interact with your Docker containers.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *