CMDSHELL vs CMD in Docker Compose: A Detailed Guide

When working with Docker, particularly when defining services in docker-compose.yml, the CMD instruction plays a crucial role in specifying the default command that gets executed when a container starts. In the context of Docker Compose, you'll often see two styles of defining this CMD: CMD (Exec Form) and CMD (Shell Form). These two approaches have subtle but important differences that can impact how your containers behave.

In this blog, we’ll explore these two forms, their usage, and when you might want to choose one over the other.


1. What is CMD in Docker?

Before diving into the difference between CMD Shell and CMD Exec forms, let's review what CMD does in Docker:

  • CMD specifies the default command that should run inside your container.

  • It provides the arguments to pass to the container's main process when no other command is specified during the container startup.

For example, in a simple Dockerfile:

FROM ubuntu

CMD ["echo", "Hello, World!"]

Here, CMD tells Docker to run the echo "Hello, World!" command whenever the container starts. If you run the container without specifying any other command, this is what will run.

2. CMD Forms in Docker

There are two main ways to specify the CMD instruction:

2.1. Exec Form (CMD)

This is the recommended and most common way to specify a command in Docker. In the Exec Form, the command and its arguments are defined as a JSON array, where each part of the command is a separate string. For example:

services:
app:
image: ubuntu
command: ["echo", "Hello, World!"]

  • The command is passed directly to the kernel for execution without involving a shell.

  • This form is more efficient and safer because no shell is involved, which means it avoids potential issues like shell word splitting or unexpected variable expansions.

Example:

CMD ["myapp", "--flag", "value"]

This will execute the myapp --flag value command without invoking a shell, reducing the risk of issues like unwanted globbing or variable expansion.

2.2. Shell Form (CMDSHELL)

In the Shell Form, the command is written as a single string, just like how you'd type it into a terminal. For example:

services:
app:
image: ubuntu
command: echo Hello, World!

  • In this case, Docker will automatically run the command using a shell (typically /bin/sh -c in Linux environments).

  • This form allows for more flexibility, such as using shell features like environment variables, wildcards, and pipes.

Example:

CMD echo "Hello, World!"

This translates to /bin/sh -c "echo 'Hello, World!'", meaning a shell is invoked before the command runs.

3. Differences Between Exec Form and Shell Form

Now that we understand the two forms, let’s break down their differences:

FeatureExec FormShell Form
Command SyntaxJSON array: ["command", "arg1"]String: "command arg1 arg2"
Shell InvolvementNo (direct execution)Yes (uses /bin/sh -c)
Use of Environment VarsRequires explicit handlingAutomatically available
Wildcard & GlobbingNo shell featuresFull shell capabilities
SecuritySafer (no shell expansion issues)Can be vulnerable to shell injection
PortabilityMore portable across environmentsShell behavior may vary

4. When to Use Each Form

Choosing between CMD Exec and CMD Shell depends on your specific use case. Here’s a quick guide:

Use CMD Exec when:

  • You want a more secure and efficient execution.

  • The command is simple and doesn’t need shell features like piping or variable expansion.

  • You’re building for portability across different environments.

  • You want to avoid shell injection vulnerabilities.

For example, when running a well-defined binary like:

CMD ["nginx", "-g", "daemon off;"]

Use CMD Shell when:

  • You need the flexibility of a shell.

  • You want to use shell-specific features like environment variables, pipes, or conditional execution (&&, ||).

  • The command you’re running requires a shell environment.

For example, when you need to execute complex commands:

CMD echo "The current user is: $USER"

In this case, the shell form is ideal because $USER is a shell environment variable that gets expanded before the command is run.

5. Example in Docker Compose

Let’s consider a simple docker-compose.yml file that uses both forms:

version: "3"
services:
app_exec:
image: busybox
command: ["echo", "Exec form: Hello, World!"]

app_shell:
image: busybox
command: echo "Shell form: Hello, World!"

  • app_exec uses the Exec Form and will directly run the echo command without invoking a shell.

  • app_shell uses the Shell Form, so the command will be passed to /bin/sh -c, allowing for shell-specific behavior (e.g., environment variable expansion).

Running docker-compose up will output:

app_exec_1 | Exec form: Hello, World!
app_shell_1 | Shell form: Hello, World!

Both containers will execute successfully, but the internal handling of the commands differs.

6. Pros and Cons

Exec Form Pros:

  • Efficiency: No shell overhead means faster execution.

  • Security: Avoids the risk of shell injection attacks or accidental expansion.

  • Portability: Works consistently across different environments (Windows, Linux).

Exec Form Cons:

  • Lack of Flexibility: No access to shell features (pipes, environment variable expansion, etc.).

Shell Form Pros:

  • Flexibility: Shell features like variable expansion, piping, and chaining (&&, ||) are available.

  • Simplicity: Easier to write simple commands, especially for developers familiar with shell syntax.

Shell Form Cons:

  • Security Risks: Potential for shell injection or unintentional command behavior.

  • Performance Overhead: Slightly slower due to the shell’s involvement.

7. Conclusion

Both Exec Form and Shell Form have their advantages and are useful in different scenarios. The Exec Form is ideal for most applications where you don’t need shell-specific features, providing better performance and security. However, the Shell Form is useful when you need the flexibility of a shell environment.

In summary:

  • Use Exec Form for straightforward, safer, and faster commands.

  • Use Shell Form when you need to take advantage of shell features like environment variables or complex command chaining.

Understanding these differences and making the right choice for your Docker Compose configurations can lead to more efficient and secure container deployments.


By mastering the difference between these two forms of CMD, you'll gain more control over how your containers run and make better decisions when writing Dockerfiles or Compose configurations.

1
Subscribe to my newsletter

Read articles from Chetan Mohanrao Mohod directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Chetan Mohanrao Mohod
Chetan Mohanrao Mohod

DevOps Engineer focused on automating workflows, optimizing infrastructure, and building scalable efficient solutions.