logoPushwant Rai
Home
Experience
Education
Projects
Achievements
Blogs

Elsewhere

GitHub
LinkedIn
hi@pushwantrai.com.np

Theme

Shell vs Exec Form in Docker CMD and ENTRYPOINT

Sep 10, 2023

#docker#dockerfile

Table of Contents

  • Introduction
  • Understanding Shell and Exec Forms
  • How Shell Form CMD Affects ENTRYPOINT
  • Signal Handling Pitfalls with Shell Form
  • Best Practices
  • Examples

Introduction

In my previous blog, we explored Docker's RUN, CMD, and ENTRYPOINT commands, focusing on their purpose and how they interact to define container behavior. This article dives deeper into the distinction between shell and exec forms, emphasizing their impact on signal handling and how improper usage can lead to unexpected issues. Let's explore and learn best practices to avoid pitfalls.

Understanding Shell and Exec Forms

Docker supports two syntaxes for specifying CMD and ENTRYPOINT: shell form and exec form.

Shell Form

  • Written as a simple string.
  • Example: CMD echo "Hello, World!"
  • Executes commands via /bin/sh by default (or /bin/bash on some systems).

Exec Form

  • Written as a JSON array of strings.
  • Example: CMD ["echo", "Hello, World!"]
  • Executes commands directly, bypassing the shell.

FeatureShell FormExec Form
SyntaxStringJSON Array
Command ExecutionThrough Shell (e.g., /bin/sh)Direct Execution
Signal HandlingManaged by ShellDirect to Process

How Shell Form CMD Affects ENTRYPOINT

When combining CMD and ENTRYPOINT, their interplay depends on the form used:

Example: Using Shell Form CMD with ENTRYPOINT

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

In this example:

  • ENTRYPOINT specifies echo as the primary process.
  • CMD should provide additional arguments to echo.

Behavior:

  • docker run my-image should run echo "Hello, World!".
  • However, because CMD is in shell form, it is interpreted as /bin/sh -c "Hello, World!". This string cannot be passed directly to ENTRYPOINT as intended, leading to unexpected behavior.

If you want to understand the behavior in more detail, you can visit the official Docker documentation.

Signal Handling Pitfalls with Shell Form

Signal handling is critical for cleanly stopping or restarting Docker containers. For example, running docker kill --signal INT should terminate the main process.

The Problem

When using shell form:

  • Docker wraps the command in a shell process (e.g., /bin/sh -c).
  • Signals (like INT, TERM) are sent to the shell process, not directly to the application.

Python Example

Consider the following Dockerfile:

FROM python:3.9-slim ENTRYPOINT ["python"] CMD "-m http.server"

Running docker kill --signal INT <container> results in:

  1. The INT signal is sent to /bin/sh.
  2. /bin/sh does not forward the signal to the Python process.
  3. The Python process continues running, even though the container should stop.

Best Practices

Always Use Exec Form

Using the exec form ensures:

  • Direct execution of the intended process.
  • Proper signal handling, as signals are sent directly to the main process.

Combine ENTRYPOINT and CMD Thoughtfully

  • Use ENTRYPOINT for the main command or application.
  • Use CMD for default arguments or configurations.

Examples

Correct Usage: Exec Form CMD and ENTRYPOINT

FROM python:3.9-slim ENTRYPOINT ["python"] CMD ["-m", "http.server"]

Behavior:

  • docker run my-image runs python -m http.server.
  • Signals are sent directly to the Python process.

Conclusion

Understanding the difference between shell and exec forms in Docker can save you from subtle bugs, especially around signal handling. By adhering to best practices and using the exec form, you ensure that your containers behave predictably, even under unexpected conditions.