Understanding Docker CMD Directive: How to Run Multiple Commands in a Container


3 views

Many Docker users encounter confusion when trying to execute multiple commands in their containers. The initial approach might look like this:

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

This doesn't work because Docker's CMD directive in JSON/exec format doesn't interpret shell operators like semicolons. The container fails to start because it's trying to execute the entire array as a single command with literal semicolons as arguments.

Docker supports two formats for CMD:

# Shell format (processed by shell)
CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

# Exec format (direct execution)
CMD ["/usr/sbin/php5-fpm"]

The working solution uses the exec format but explicitly invokes a shell to interpret the commands:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Here are some common patterns for running multiple commands:

# Using shell to chain commands
CMD ["sh", "-c", "command1 && command2"]

# Background process with follow-up command
CMD ["sh", "-c", "service nginx start & service php-fpm start"]

# Environment variable substitution
CMD ["sh", "-c", "echo $HOME && ls -la"]

When dealing with multiple services in a container:

  • Consider using a process manager like supervisord
  • For complex setups, split into multiple containers
  • Always test your CMD syntax with docker run before building the final image

The same principle applies to docker-compose.yml:

services:
  app:
    command: sh -c "/etc/init.d/nullmailer start && /usr/sbin/php5-fpm"

Remember that while it's possible to run multiple commands, Docker's philosophy favors single-process containers for better isolation and scalability.


When trying to run multiple commands in a Docker container, many developers encounter unexpected behavior. The natural approach might be:

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

But this fails because Docker interprets the semicolon as a literal argument rather than a shell operator.

Docker's CMD directive has two fundamental formats:

1. JSON array format (exec form):

CMD ["executable","param1","param2"]

This directly executes the binary without shell processing, meaning:

  • No environment variable expansion
  • No shell operators (;, &, |, etc.)
  • No command substitution

2. Shell format:

CMD command param1 param2

To execute multiple commands, you have several proper approaches:

Solution 1: Explicit shell invocation

CMD ["sh", "-c", "/etc/init.d/nullmailer start && /usr/sbin/php5-fpm"]

Solution 2: Using a shell script

# Dockerfile
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]

Solution 3: For complex scenarios

CMD ["sh", "-c", "service nullmailer start && \
     /usr/sbin/php5-fpm --nodaemonize"]

Example 1: Database initialization

CMD ["sh", "-c", "mysqld_safe & \
     while ! mysqladmin ping --silent; do sleep 1; done \
     && mysql -uroot -e 'CREATE DATABASE IF NOT EXISTS app'"]

Example 2: Multiple services in one container

CMD ["sh", "-c", "nginx && php-fpm7"]
  • Always prefer explicit shell invocation over assuming shell behavior
  • For production, consider using proper process managers (supervisord, s6)
  • Remember that shell-form CMD runs under /bin/sh -c
  • Debug with: docker run -it --entrypoint sh yourimage