Docker Compose — Command and Entrypoint

2 minute read

Every Docker image has a default command that runs when the container starts. Compose lets you override both the command and the entrypoint to customize container behavior.


How Docker Commands Work

┌──────────────────────────────────────┐
│         Container Startup            │
│                                      │
│  ENTRYPOINT  +  CMD  =  Full Command│
│                                      │
│  Example (nginx):                    │
│  ENTRYPOINT: none                    │
│  CMD: ["nginx", "-g", "daemon off;"]│
│                                      │
│  Example (postgres):                 │
│  ENTRYPOINT: ["docker-entrypoint.sh"]│
│  CMD: ["postgres"]                   │
└──────────────────────────────────────┘
  • ENTRYPOINT: The executable to run (hard to override)
  • CMD: Default arguments passed to entrypoint (easy to override)

Overriding command:

The command: key overrides the CMD instruction from the Dockerfile:

services:
  # Default: nginx starts and serves files
  web:
    image: nginx:alpine

  # Override: run a custom command instead
  web-custom:
    image: nginx:alpine
    command: nginx -g "daemon off;" -c /etc/nginx/custom.conf

  # Shell form — runs through /bin/sh
  debug:
    image: alpine
    command: sh -c "echo Hello && sleep 3600"

  # Exec form — runs directly (preferred)
  debug2:
    image: alpine
    command: ["sh", "-c", "echo Hello && sleep 3600"]

Common Use Cases

services:
  # Run database with custom flags
  db:
    image: postgres:16
    command: postgres -c shared_buffers=256MB -c max_connections=200

  # Run app in dev mode
  api:
    build: .
    command: npm run dev

  # Run migrations before starting
  api:
    build: .
    command: sh -c "npm run migrate && npm start"

Overriding entrypoint:

The entrypoint: key overrides the ENTRYPOINT instruction:

services:
  # Override entrypoint to debug
  db:
    image: postgres:16
    entrypoint: ["sh", "-c", "echo 'Container started' && exec docker-entrypoint.sh postgres"]

Warning: Overriding entrypoint: can break images that depend on their entrypoint script (like PostgreSQL, MySQL). Use command: instead when possible.


Shell Form vs Exec Form

Form Syntax Shell Processing Signal Handling
Shell command: echo hello ✅ Yes (sh -c) ❌ Signals go to shell
Exec command: ["echo", "hello"] ❌ No ✅ Signals go to process
services:
  # Shell form — supports pipes, variables, &&
  app:
    command: sh -c "echo $HOSTNAME && npm start"

  # Exec form — better signal handling, cleaner
  app:
    command: ["npm", "start"]

Best practice: Use exec form for simple commands, shell form when you need pipes (|), chaining (&&), or variable expansion.


Summary

Option Overrides Use When
command: Dockerfile CMD Custom arguments or dev mode
entrypoint: Dockerfile ENTRYPOINT Rarely — can break images
Shell form command: npm start Need shell features
Exec form command: ["npm", "start"] Clean signal handling

Quick Reference

Item Detail
command: npm run dev Override CMD for dev mode
command: [‘npm’, ‘start’] Exec form — better signal handling
entrypoint: Override ENTRYPOINT — use carefully
Shell form Supports pipes, &&, variables
Exec form Direct execution, proper signal handling
Best practice Prefer command: over entrypoint: