$docker.recipes
·10 min read·Updated August 2025

Migrating from Docker Run to Docker Compose: A Practical Guide

Step-by-step guide to converting your docker run commands into clean, maintainable Docker Compose files with real-world examples.

dockerdocker-composemigrationtutorial

01Why Migrate to Docker Compose?

If you've been running Docker containers with long docker run commands, you know the pain: scrolling through bash history to find the right command, forgetting which flags you used, and trying to remember exact volume paths. I maintained a text file with 30+ docker run commands for two years before switching to Docker Compose. The migration took a single afternoon, and I haven't used docker run for a persistent service since. Docker Compose gives you version-controlled, reproducible, self-documenting infrastructure. Instead of a 200-character command, you have a readable YAML file anyone can understand.

02The Translation Pattern

Every docker run flag maps directly to a Docker Compose property. Here's a real example:
[terminal]
1# This docker run command:
2docker run -d \
3 --name nextcloud \
4 --restart unless-stopped \
5 -p 8080:80 \
6 -v nextcloud_data:/var/www/html \
7 -e POSTGRES_HOST=db \
8 -e POSTGRES_PASSWORD=secret \
9 --network my_network \
10 nextcloud:29

03Becomes Clean YAML

The same configuration in Docker Compose:
[docker-compose.yml]
1services:
2 nextcloud: # --name
3 image: nextcloud:29 # image name
4 restart: unless-stopped # --restart
5 ports:
6 - "8080:80" # -p
7 volumes:
8 - nextcloud_data:/var/www/html # -v
9 environment: # -e
10 - POSTGRES_HOST=db
11 - POSTGRES_PASSWORD=secret
12 networks: # --network
13 - my_network
14
15volumes:
16 nextcloud_data:
17
18networks:
19 my_network:

Use 'docker compose convert' to validate your compose file syntax. It shows the fully resolved configuration with all defaults filled in.

04Complete Flag Reference

Common docker run flags and their Compose equivalents: --name → service key (or container_name) -d → default in Compose -p 8080:80 → ports: ["8080:80"] -v /host:/container → volumes: ["/host:/container"] -e VAR=value → environment: [VAR=value] --env-file → env_file: [.env] --network → networks: [name] --restart → restart: unless-stopped --memory 512m → deploy.resources.limits.memory: 512M --cpus 1.5 → deploy.resources.limits.cpus: "1.5" --user 1000:1000 → user: "1000:1000" --read-only → read_only: true --privileged → privileged: true --cap-add/drop → cap_add/cap_drop The biggest Compose advantage is depends_on with health checks, which has no docker run equivalent. You can ensure databases are ready before starting applications. Browse our full-stacks category for multi-container examples.

About the Author

Frank Pegasus

DevOps engineer and self-hosting enthusiast with over a decade of experience running containerized workloads in production. Creator of docker.recipes.