Docker Run To Docker Compose

Written by

in

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

  • Reproducibility — the same file produces the same environment every time, on any machine.
  • Version control — you can commit the compose file to git and track every change to your infrastructure.
  • Multi-container orchestration — services can reference each other by name instead of by IP address.
  • Simpler commandsdocker compose up, docker compose down, and docker compose logs replace multiple long docker run invocations.
  • Built-in networking — Compose automatically creates a dedicated network so containers can talk to each other without manual --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.

  • Forgetting named volumes — a bind mount in 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.
  • Port order confusion — Compose uses the same host:container order as docker run -p, but it is easy to transpose the two when hand-editing YAML, so double-check each mapping.
  • Missing restart policies — a 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.
  • Network name collisions — Compose automatically names its network after the project directory. If you rename the folder, the network name changes too, which can break external references. Set 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.

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *