Stop managing long docker run commands! Convert them to maintainable Compose files.

Basic conversions

Common flag mappings:

# Docker run command
docker run -d \
  --name myapp \
  -p 3000:3000 \
  -e NODE_ENV=production \
  -e API_KEY=secret123 \
  -v $(pwd)/data:/app/data \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --restart unless-stopped \
  myapp:latest

Becomes:

services:
  myapp:
    image: myapp:latest
    container_name: myapp
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      API_KEY: secret123
    volumes:
      - ./data:/app/data
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

Network configurations

# Host network
docker run --network host nginx

# Custom network
docker run --network mynet --ip 172.20.0.5 app

# Network alias
docker run --network mynet --network-alias db postgres

Compose equivalent:

services:
  nginx:
    image: nginx
    network_mode: host

  app:
    image: app
    networks:
      mynet:
        ipv4_address: 172.20.0.5

  postgres:
    image: postgres
    networks:
      mynet:
        aliases:
          - db

networks:
  mynet:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

Resource limits

docker run -d \
  --memory="2g" \
  --memory-swap="4g" \
  --cpus="1.5" \
  --cpu-shares="512" \
  myapp

Becomes:

services:
  myapp:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: "1.5"
          memory: 2G
        reservations:
          cpus: "0.5"
          memory: 1G
    mem_swappiness: 60
    cpu_shares: 512

User and working directory

docker run \
  --user 1000:1000 \
  --workdir /app \
  --entrypoint /custom-entrypoint.sh \
  myapp npm start

Becomes:

services:
  myapp:
    image: myapp
    user: "1000:1000"
    working_dir: /app
    entrypoint: /custom-entrypoint.sh
    command: npm start

Advanced security options

docker run \
  --privileged \
  --cap-add SYS_ADMIN \
  --cap-drop ALL \
  --security-opt apparmor=unconfined \
  --read-only \
  --tmpfs /tmp:size=100M \
  myapp

Becomes:

services:
  myapp:
    image: myapp
    privileged: true
    cap_add:
      - SYS_ADMIN
    cap_drop:
      - ALL
    security_opt:
      - apparmor:unconfined
    read_only: true
    tmpfs:
      - /tmp:size=100M

Complex real-world example

Converting a database with initialization:

docker run -d \
  --name postgres \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=mydb \
  -e POSTGRES_USER=admin \
  -v postgres_data:/var/lib/postgresql/data \
  -v $(pwd)/init.sql:/docker-entrypoint-initdb.d/init.sql \
  -p 5432:5432 \
  --health-cmd "pg_isready -U admin" \
  --health-interval 10s \
  --health-timeout 5s \
  --health-retries 5 \
  --restart always \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  postgres:15

Becomes:

services:
  postgres:
    image: postgres:15
    container_name: postgres
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: mydb
      POSTGRES_USER: admin
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  postgres_data:

Common gotchas

# WRONG: Using command for everything
services:
  app:
    image: ubuntu
    command: /bin/bash -c "apt update && apt install -y curl && curl http://example.com"

# RIGHT: Use entrypoint for shell, command for args
services:
  app:
    image: ubuntu
    entrypoint: ["/bin/bash", "-c"]
    command: ["apt update && apt install -y curl && curl http://example.com"]

# BETTER: Use custom image
services:
  app:
    build: .
    command: ["curl", "http://example.com"]

Pro tip

Use Docker Compose’s built-in conversion tool:

# Convert a running container to Compose format
docker compose alpha generate [container-name-or-id]

# Example: Convert a running container
docker run -d --name myapp -p 3000:3000 -e NODE_ENV=production myapp:latest
docker compose alpha generate myapp > compose.yml

# Generate from multiple containers
docker compose alpha generate web db cache > stack.yml

# With project name
docker compose alpha generate --name myproject web db > compose.yml

This experimental command automatically converts running containers to Compose format!

Clean, maintainable, and version-controlled!

Further reading