Docker Run To Compose

Written by

in

Migrating Docker Run To 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.

Managing a growing list of docker run flags is a common signal that it’s time to move docker run to compose. Converting a long imperative command into a declarative docker-compose.yml file makes your setup reproducible, versionable, and far easier to hand off to another engineer or CI pipeline.

Why Move Docker Run To Compose

A single docker run command works fine for a quick test, but once you add volumes, environment variables, port mappings, restart policies, and networks, the command becomes unreadable and easy to get wrong. Every time you run it, you risk a typo that silently changes behavior — a missing -v, a reordered -p, or a forgotten --restart flag.

Moving docker run to compose solves this by capturing the entire configuration in one YAML file that lives in your repository, next to your application code. Instead of remembering a dozen flags, you run:

docker compose up -d

and the exact same environment comes up every time, on every machine.

Key Benefits Of Compose Over Raw Run Commands

  • Version control — the file can be committed to git and reviewed like any other code change.
  • Repeatability — no risk of forgetting a flag between deployments.
  • Multi-container orchestration — a compose file can define an app, database, and cache together, with dependency ordering.
  • Readable diffs — changing a port or environment variable is a one-line diff instead of hunting through shell history.
  • Easier onboarding — a new team member runs one command instead of copy-pasting a long shell one-liner.
  • Understanding The Anatomy Of A Docker Run Command

    Before you convert docker run to compose, it helps to break a typical command into its parts. Consider this example:

    docker run -d \
      --name web_app \
      -p 8080:80 \
      -e NODE_ENV=production \
      -v app_data:/data \
      --restart unless-stopped \
      --network app_net \
      myorg/web-app:1.4

    Each flag maps to a specific compose key:

  • -d (detached mode) has no direct compose key — docker compose up -d handles it at invocation time.
  • --name maps to container_name.
  • -p maps to ports.
  • -e maps to environment.
  • -v maps to volumes.
  • --restart maps to restart.
  • --network maps to networks.
  • the image tag maps directly to image.
  • Once you can identify each of these pairs, the actual translation from docker run to compose becomes mostly mechanical.

    Mapping Common Flags To Compose Keys

    Here’s a more complete reference table you’ll use repeatedly:

    | docker run flag | Compose key |
    |—|—|
    | --name | container_name |
    | -p host:container | ports: |
    | -e KEY=VALUE | environment: |
    | -v host:container | volumes: |
    | --network | networks: |
    | --restart | restart: |
    | --link | depends_on: (with a real network, not legacy links) |
    | --memory / --cpus | deploy.resources.limits (or mem_limit/cpus in Compose v2) |

    Step-By-Step: Converting Docker Run To Compose

    The cleanest way to move docker run to compose is to work through the command flag by flag and build the YAML incrementally rather than trying to write the whole file from memory.

    Step 1: Create The Base Service Definition

    Start with the image and a service name. The service name doesn’t have to match --name, though keeping them aligned reduces confusion:

    services:
      web_app:
        image: myorg/web-app:1.4
        container_name: web_app

    Step 2: Add Ports, Environment, And Volumes

    Translate each remaining flag into its corresponding block. For anything beyond a couple of environment variables, prefer an env_file over inline environment entries — it keeps secrets out of the compose file itself, a practice covered in more depth in this guide to managing Compose environment variables the right way.

    services:
      web_app:
        image: myorg/web-app:1.4
        container_name: web_app
        restart: unless-stopped
        ports:
          - "8080:80"
        environment:
          - NODE_ENV=production
        volumes:
          - app_data:/data
        networks:
          - app_net
    
    volumes:
      app_data:
    
    networks:
      app_net:

    That’s the full docker run to compose translation for the example above — every flag from the original command now has a home in the YAML file, and the stack starts with docker compose up -d.

    Step 3: Validate And Run

    Before trusting a converted file in production, validate the syntax and confirm the config resolves as expected:

    docker compose config
    docker compose up -d
    docker compose ps

    docker compose config prints the fully resolved configuration (with any .env substitutions applied), which is the fastest way to catch a typo before containers actually start.

    Handling Multi-Container Setups When You Move Docker Run To Compose

    Real applications rarely run as a single container. If you’ve been starting an app container and a database with two separate docker run commands (and manually creating a shared network), compose replaces both commands — and the manual networking step — with one file.

    services:
      app:
        image: myorg/web-app:1.4
        ports:
          - "8080:80"
        depends_on:
          - db
        environment:
          - DATABASE_URL=postgres://user:pass@db:5432/appdb
    
      db:
        image: postgres:16
        environment:
          - POSTGRES_USER=user
          - POSTGRES_PASSWORD=pass
          - POSTGRES_DB=appdb
        volumes:
          - db_data:/var/lib/postgresql/data
    
    volumes:
      db_data:

    Services in the same compose file share a default network automatically, so app can reach db by service name — no manual docker network create step required, which is one of the more tedious things you had to do by hand with separate docker run invocations. For a deeper walkthrough of getting a database container production-ready this way, see this Postgres Docker Compose setup guide.

    depends_on controls startup order but not readiness — the database container will be running before it’s necessarily accepting connections. For anything that matters, add a healthcheck rather than assuming the dependency is ready.

    Common Pitfalls When You Convert Docker Run To Compose

    A few mistakes come up repeatedly when teams first move docker run to compose:

  • Forgetting named volumes need declaring — a volumes: entry under a service references a name, but that name must also be listed under the top-level volumes: key or Compose will create an anonymous one instead.
  • Dropping -it silently — interactive/TTY flags don’t have a direct compose equivalent for up; use docker compose run --rm -it <service> <command> for one-off interactive sessions instead.
  • Assuming --link still matters — legacy --link is superseded by Compose’s built-in service-name DNS resolution; don’t try to replicate it explicitly.
  • Copy-pasting host paths verbatim — a bind mount that worked on your laptop (-v /Users/you/app:/app) won’t work on a server; use a relative path or a named volume instead.
  • Not rebuilding after a Dockerfile changedocker compose up -d won’t pick up a changed Dockerfile unless you also run docker compose build or add --build. This is covered in detail in the guide on rebuilding a Compose stack correctly.
  • If you’re unsure whether a given project even needs a Dockerfile versus a plain image reference in your compose file, this comparison of Dockerfile vs Docker Compose is a useful primer before you go further.

    Debugging A Failed Conversion

    When a converted stack doesn’t behave the same as the original docker run command did, the fastest diagnostic loop is:

    docker compose logs -f app
    docker compose exec app env
    docker compose config

    Comparing the output of docker compose exec app env against the environment variables you passed to the original docker run -e flags will usually surface a missing or misspelled key immediately. For a fuller debugging workflow, see this Compose logs debugging guide.

    Managing Secrets After You Move Docker Run To Compose

    One thing a raw docker run -e command can’t do cleanly is separate secrets from configuration. Once you’ve committed to compose, it’s worth also adopting Compose’s secrets handling (or at minimum a gitignored .env file) rather than leaving passwords inline in the YAML you now version-control. This Docker Compose secrets management guide walks through the options in more detail.

    If your stack includes a cache layer alongside the app and database, the same conversion principles apply — flags become keys, and the container joins the same default network automatically, as shown in this Redis Docker Compose guide.

    Where To Run Your Converted Compose Stack

    A compose file is portable by design, but it still needs a host. If you’re moving off a local machine and onto a small production server, a straightforward VPS is usually sufficient for a single compose stack — you don’t need a full orchestration platform unless you’re running many services across multiple machines. Providers like DigitalOcean and Hetzner offer VPS instances that run Docker and Compose out of the box with a standard Linux image.

    If your workload grows beyond what a single compose file can reasonably manage — multiple hosts, automated failover, rolling updates — that’s the point to evaluate a step up, which is covered in this comparison of Kubernetes vs Docker Compose.


    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 docker compose up replace docker run entirely?
    For anything with more than a couple of flags, yes. docker run still has a place for quick, disposable one-off containers, but once you need repeatability across environments, moving docker run to compose is worth the small upfront effort.

    Can I convert a docker run command to compose automatically?
    There’s no universally reliable built-in converter, since some flags (like -it) don’t map cleanly to up. Manual, flag-by-flag translation as shown above is more reliable than trusting an automated tool blindly, especially for anything going into production.

    What happens to --rm when I move docker run to compose?
    --rm removes the container after it exits, which is mainly useful for one-off commands. Use docker compose run --rm <service> for the equivalent behavior; a long-running services: entry under up isn’t meant to be ephemeral in the same way.

    Do I need a Dockerfile to use Compose?
    No. If you’re just running a pre-built image with different flags, image: in the compose file is enough. A build: key (pointing at a Dockerfile) is only needed if Compose should build the image itself rather than pull it from a registry.

    Conclusion

    Converting docker run to compose is mostly a mechanical exercise once you understand how each CLI flag maps to a YAML key — ports, environment variables, volumes, networks, and restart policies all have direct equivalents. The real payoff isn’t the conversion itself, but what it enables afterward: a config file you can commit, review, and reuse across environments instead of re-typing (or mis-typing) a long shell command. For the official reference on every available compose key, see the Docker Compose file reference and the broader Docker documentation for anything not covered here.

    Comments

    Leave a Reply

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