Migrating from Docker Run to Docker Compose
Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.
If you have been managing containers with a growing pile of docker run commands, you already know the pain: long flags, forgotten port mappings, and no easy way to restart everything the same way twice. Moving from docker run to docker compose gives you a single, version-controlled file that describes your entire container setup, and it is one of the most useful upgrades a small infrastructure can make. This guide walks through why the migration matters and exactly how to do it, step by step.
Why Move From Docker Run to Docker Compose
A single docker run command is fine for a quick test. The trouble starts when you have more than one container, or when a container needs specific volumes, networks, environment variables, and restart policies. Every time you need to recreate that container, you have to remember (or dig up in your shell history) the exact flags you used.
Docker Compose solves this by moving the entire configuration into a docker-compose.yml (or compose.yaml) file. Instead of typing out a long command, you run docker compose up -d and Compose reads the file, builds or pulls the images, creates the network, and starts every service in the correct order.
Key Benefits of Docker Compose Over Raw Docker Run
docker compose up, docker compose down, and docker compose logs replace multiple long docker run invocations.--network flags.Mapping Docker Run Flags to Docker Compose Syntax
The core work of migrating from docker run to docker compose is translating command-line flags into YAML keys. Most flags have a direct, one-to-one equivalent, which makes the docker run to docker compose conversion mostly mechanical once you know the mapping.
Common Flag-to-YAML Translations
| docker run flag | Compose key |
|—|—|
| -p 8080:80 | ports: ["8080:80"] |
| -v /data:/app/data | volumes: ["/data:/app/data"] |
| -e KEY=value | environment: [KEY=value] |
| --name mycontainer | container_name: mycontainer |
| --restart unless-stopped | restart: unless-stopped |
| --network mynet | networks: [mynet] |
| -d (detached) | implied by docker compose up -d |
Worked Example: From Command to File
Suppose you have been starting a simple web app with a command like this:
docker run -d
--name myapp
-p 8080:80
-e NODE_ENV=production
-v myapp_data:/app/data
--restart unless-stopped
myorg/myapp:latest
Translating this line by line into Compose syntax gives you a clean, readable file:
services:
myapp:
image: myorg/myapp:latest
container_name: myapp
ports:
- "8080:80"
environment:
- NODE_ENV=production
volumes:
- myapp_data:/app/data
restart: unless-stopped
volumes:
myapp_data:
Once this file exists, the container is started with docker compose up -d, and every future engineer on the project can read exactly what configuration is running without reverse-engineering a shell script.
Step-by-Step Docker Run to Docker Compose Migration
Moving an existing set of docker run commands to Compose is a repeatable process. Following these steps for each container keeps the migration low-risk and easy to verify.
Step 1: Inventory Your Existing Containers
Before writing any YAML, list every container you currently run and the exact flags used to start it. You can pull this information from docker inspect if you no longer have the original commands:
docker inspect myapp --format '{{json .Config}}' | python3 -m json.tool
This shows the image, environment variables, exposed ports, and mounted volumes for a running container, which is useful when the original docker run command has been lost.
Step 2: Write One Service Per Container
Create a docker-compose.yml file and add one entry under services: for each container in your inventory, following the flag mapping table above. If two containers need to talk to each other (for example, an app and a database), Compose’s default network lets them reach each other using the service name as a hostname — no more hardcoded IP addresses or --link flags.
Step 3: Test, Then Cut Over
Bring the new stack up alongside the old containers first, using different host ports if needed, and confirm the application behaves the same way. Once verified, stop the old containers and start the Compose-managed versions on the real ports:
docker compose up -d
docker compose ps
If something goes wrong, docker compose down cleanly removes the containers and network Compose created, which is a much safer rollback path than manually tracking down every container you started by hand.
Handling Multi-Container Applications
The biggest practical reason teams move from docker run to docker compose is multi-container coordination. A typical web application stack might include an app server, a database, and a cache — three separate docker run commands that all need to agree on network names, startup order, and shared volumes.
Defining Service Dependencies
Compose’s depends_on key lets you express startup order directly in the file:
services:
web:
image: myorg/myapp:latest
depends_on:
- db
ports:
- "8080:80"
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=changeme
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
depends_on controls the order containers are started in, not whether the dependent service is actually ready to accept connections — for that, add a healthcheck or handle retries in your application code. If you are running Postgres specifically, our Postgres Docker Compose setup guide covers healthchecks and data persistence in more detail.
Managing Secrets and Environment Files
Hardcoding passwords directly in docker run -e commands is a common bad habit that Compose makes easy to fix. Instead of inline environment variables, reference an .env file or Compose’s secrets: block. For a deeper walkthrough of both approaches, see our guides on managing Compose environment variables and Docker Compose secrets management.
Common Pitfalls When Migrating to Docker Compose
Most migration problems come from small mismatches between the old command and the new file, not from Compose itself.
docker run -v /host/path:/container/path is straightforward, but a named volume (-v myvolume:/path) needs a matching volumes: top-level declaration in the compose file, or Compose will create an anonymous volume instead.host:container order as docker run -p, but it is easy to transpose the two when hand-editing YAML, so double-check each mapping.docker run command with no --restart flag and a Compose service with no restart: key behave the same way (no automatic restart), but many teams assume Compose restarts by default. It does not; set restart: unless-stopped explicitly if that is the behavior you want.name: under a top-level networks: block if you need a stable name.Once you’re comfortable with these basics, tools like Docker Compose Rebuild and Docker Compose Logs become part of the regular workflow for keeping a migrated stack healthy.
Docker Run to Docker Compose: When Compose Isn’t Enough
Docker Compose is built for running containers on a single host. If your workload grows to the point where you need multiple hosts, automated failover, or rolling updates across a cluster, Compose is no longer the right tool — that’s the point where teams typically look at Kubernetes instead. Our comparison of Kubernetes vs Docker Compose covers where that line usually falls. For most small-to-medium projects — a handful of services on a single VPS — Compose remains the simpler, easier-to-maintain choice, and there is no need to reach for a full orchestrator just because it exists.
If you are hosting this stack on a virtual server, providers like DigitalOcean offer straightforward VPS plans that work well for a Compose-managed application, without the operational overhead of a full cluster.
Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.
FAQ
Does migrating from docker run to docker compose change how my containers behave?
No. Compose is a orchestration layer on top of the same Docker Engine APIs that docker run uses. As long as the flags are translated correctly into the compose file, the resulting containers run identically — same image, same network behavior, same resource usage.
Can I run docker run and docker compose containers on the same host at the same time?
Yes. They share the same Docker daemon, so nothing stops you from having some containers started manually and others managed by Compose. This is actually a useful way to migrate gradually, testing one service in Compose while leaving the rest as-is.
What happens to my data volumes during the migration?
If you reference the same named volume in your compose file that your original docker run -v myvolume:/path command used, Compose will attach to the existing volume rather than creating a new one — no data is lost. Bind mounts to host directories work the same way, since the data lives on the host filesystem independent of the container.
Do I need to remove the old containers before switching to Compose?
It’s a good idea to stop and remove the old docker run containers once you’ve verified the Compose version works, mainly to avoid port conflicts. You can do this with docker stop and docker rm, or simply docker compose down if you first ran the old container under the same name Compose expects to manage.
Conclusion
Moving from docker run to docker compose is one of the highest-value, lowest-risk changes you can make to a small container deployment. It replaces fragile, easy-to-forget shell commands with a single declarative file that documents your infrastructure, works the same way across machines, and scales naturally as you add more services. Start by inventorying your current docker run commands, translate each one using the flag mapping above, and test the new file alongside your existing setup before cutting over. For further reading on the underlying tool, the official Docker Compose documentation and Docker CLI reference are the most reliable sources for flags and behavior that change between releases.
Leave a Reply