Convert docker run to docker-compose.yml (Step-by-Step)
You started a container with a docker run command. Now you need to check it into version control, share it with your team, or add it to a multi-service stack. Converting it to a docker-compose.yml file is the right move - here is the complete flag-by-flag mapping.
Why Move from docker run to docker-compose
A single docker run command gets unwieldy fast. Add ports, volumes, environment variables, a restart policy, and a custom network, and you have a command that wraps across multiple lines and is impossible to remember. Problems with relying on docker run:
- Not version-controllable as a single, readable file
- Difficult to share with team members or document
- No built-in dependency ordering between containers
- Must be re-typed or stored in a shell script
- Does not integrate cleanly with CI/CD pipelines
docker-compose.yml solves all of these. It is declarative, readable, version-controlled YAML that describes your entire service configuration in one place.
The Basic Structure of docker-compose.yml
Before mapping flags, understand the YAML structure:
version: "3.9" # Compose file format version
services: # Define one or more services
my-service: # Service name (replaces --name)
image: nginx:latest # The Docker image
# ... all other config goes here
volumes: # Named volumes (optional)
my-data:
networks: # Custom networks (optional)
my-network:
Step-by-Step: Real Example Conversion
Let's convert a real-world docker run command for a web application with a database:
docker run -d \
--name my-app \
-p 3000:3000 \
-e NODE_ENV=production \
-e DATABASE_URL=postgres://user:pass@db:5432/myapp \
-v /host/uploads:/app/uploads \
--restart unless-stopped \
--network app-network \
my-org/my-app:latest
The equivalent docker-compose.yml:
version: "3.9"
services:
my-app:
image: my-org/my-app:latest
container_name: my-app # --name
ports:
- "3000:3000" # -p 3000:3000
environment:
NODE_ENV: production # -e NODE_ENV=production
DATABASE_URL: postgres://user:pass@db:5432/myapp
volumes:
- /host/uploads:/app/uploads # -v
restart: unless-stopped # --restart unless-stopped
networks:
- app-network # --network
networks:
app-network:
external: true # use existing network
Complete Flag Mapping Cheat Sheet
-p / --publish: Port Mapping
# docker run
-p 80:80
-p 127.0.0.1:8080:8080 # bind to specific interface
-p 3000 # random host port
# docker-compose.yml
ports:
- "80:80"
- "127.0.0.1:8080:8080"
- "3000" # random host port
-v / --volume: Volume Mounts
# docker run
-v /host/path:/container/path # bind mount
-v /host/path:/container/path:ro # read-only
-v myvolume:/container/path # named volume
# docker-compose.yml
volumes:
- /host/path:/container/path
- /host/path:/container/path:ro
- myvolume:/container/path
# Named volume must also be declared at top level
volumes:
myvolume:
-e / --env: Environment Variables
# docker run
-e KEY=value
-e KEY # inherit from host shell
--env-file .env
# docker-compose.yml
environment:
KEY: value
KEY: # inherit from host shell (no value)
# Or use env_file
env_file:
- .env
--name: Container Name
# docker run
--name my-container
# docker-compose.yml
container_name: my-container
--restart: Restart Policy
# docker run options
--restart no # default
--restart always
--restart unless-stopped
--restart on-failure
--restart on-failure:3 # max 3 retries
# docker-compose.yml
restart: no
restart: always
restart: unless-stopped
restart: on-failure
--network: Networks
# docker run
--network my-network
--network host
--network none
# docker-compose.yml
networks:
- my-network
# Top-level networks section
networks:
my-network: # creates new network
driver: bridge
existing-network:
external: true # use pre-existing network
-d / --detach: Background Mode
The -d flag has no equivalent in docker-compose because docker compose up -d runs all services detached by default.
--entrypoint: Override Entrypoint
# docker run
--entrypoint /bin/sh
# docker-compose.yml
entrypoint: /bin/sh
# Or as array:
entrypoint: ["/bin/sh", "-c"]
Command Override (positional argument after image)
# docker run
docker run nginx:latest nginx -g "daemon off;"
# docker-compose.yml
command: nginx -g "daemon off;"
# Or as array:
command: ["nginx", "-g", "daemon off;"]
--user: Run as Specific User
# docker run
--user 1000:1000
--user www-data
# docker-compose.yml
user: "1000:1000"
--hostname: Container Hostname
# docker run
--hostname mycontainer
# docker-compose.yml
hostname: mycontainer
--add-host: /etc/hosts Entries
# docker run
--add-host host.docker.internal:host-gateway
# docker-compose.yml
extra_hosts:
- "host.docker.internal:host-gateway"
--cap-add / --cap-drop: Linux Capabilities
# docker run
--cap-add NET_ADMIN
--cap-drop ALL
# docker-compose.yml
cap_add:
- NET_ADMIN
cap_drop:
- ALL
Convert docker run to Compose Automatically
Paste your docker run command and get a ready-to-use docker-compose.yml in seconds. Free, no install required.
Multi-Container Example: App + Database + Redis
The real power of Compose is managing multiple containers together. Here is a complete example for a Node.js app with PostgreSQL and Redis:
version: "3.9"
services:
app:
image: my-org/my-app:latest
container_name: app
ports:
- "3000:3000"
environment:
NODE_ENV: production
DATABASE_URL: postgres://appuser:secret@db:5432/appdb
REDIS_URL: redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
networks:
- app-net
db:
image: postgres:15-alpine
container_name: postgres
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pg-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-net
redis:
image: redis:7-alpine
container_name: redis
volumes:
- redis-data:/data
restart: unless-stopped
networks:
- app-net
volumes:
pg-data:
redis-data:
networks:
app-net:
driver: bridge
Secrets and Sensitive Environment Variables
Never hardcode sensitive values in docker-compose.yml. Use a .env file at the same level as the compose file:
# .env file (add to .gitignore)
POSTGRES_PASSWORD=my-secret-password
API_KEY=sk-prod-abc123
# docker-compose.yml references .env automatically
services:
db:
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
app:
environment:
API_KEY: ${API_KEY}
You can also use Docker Secrets for production Swarm deployments, which mounts secrets as files in /run/secrets/ rather than environment variables.
Useful docker compose Commands
# Start all services in background
docker compose up -d
# Start and rebuild images
docker compose up -d --build
# Stop and remove containers
docker compose down
# Stop, remove containers, AND delete volumes
docker compose down -v
# View logs for all services
docker compose logs -f
# View logs for a specific service
docker compose logs -f app
# Exec into a running container
docker compose exec app sh
# Scale a service
docker compose up -d --scale app=3
FAQ
What version should I use in docker-compose.yml?
For new projects, use version: "3.9". It supports all modern features including healthchecks, deploy configs for Swarm, and depends_on with conditions. If you are not using Swarm, the version key is actually optional in Compose v2 (the current default) - but including it is good practice for clarity.
What is the difference between docker-compose and docker compose?
docker-compose (hyphen) is the legacy Python-based standalone binary (v1). docker compose (space) is the modern Go plugin built into Docker Desktop and Docker Engine (v2). The YAML format is the same between both. The v2 plugin is the current standard and should be used for all new projects.
How do I handle depends_on properly?
depends_on alone only waits for the container to start, not for the service inside it to be ready. For a database, the container starts before PostgreSQL finishes initializing. Use depends_on with condition: service_healthy and define a healthcheck on the dependency service. This ensures your app container only starts once the database is actually accepting connections.
Can I use a Dockerfile instead of an image?
Yes. Replace image: with a build: section pointing to your Dockerfile's directory. Run docker compose up -d --build to rebuild the image when your Dockerfile or source changes.
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
NODE_VERSION: "20"
How do I mount configuration files into a container?
Use a bind mount with a specific file path. This is more precise than mounting an entire directory and avoids accidentally overwriting container files:
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
Use our free tool here → Docker Run to Compose Converter
Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.