docker compose up does five things in a specific order. Most debugging becomes trivial once you know which step failed: image resolution, network/volume creation, container creation, or the actual app process.
Related: Docker Compose Restart Policy, Docker Compose Stop, Podman vs Docker, Docker manifest unknown runbook.
Quick answer: docker compose up creates (or updates) the resources from your compose.yml and starts your services. By default it won’t always pull newer images or rebuild your Dockerfile changes. Use --pull and --build when you need to force that.
Need to stop and remove containers after youre done? Read: Docker Compose Down. And if youre considering -v / --volumes, use this safety guide first: Docker Compose Down -v / –volumes (what it deletes).
Compose V2 vs V1 (quick sanity check)
This post assumes Compose V2: docker compose ... (plugin). If you use docker-compose (V1, deprecated), some flags and behaviors differ. The mental model is the same.
What exactly happens when you run docker compose up?
Here’s the real order of operations. If you’re surprised by restarts, stale code, or missing env vars, one of these steps is where the mismatch happens.
- Loads configuration:
compose.yml, overrides,.env, profiles. - Resolves images: decides whether it can reuse a local image, pull, or build.
- Creates/updates infrastructure: networks and volumes referenced by services.
- Creates/recreates containers: based on config changes (ports, env, mounts, image digest, etc.).
- Starts containers: then shows logs (unless
-d).
Image resolution: when does it pull vs build?
This is where most ‘why didn’t my change apply?’ questions live. Default behavior is conservative: if an image is already present locally, Compose typically won’t pull a newer one unless you tell it to.
- If your service uses
image:only,upwill use the local copy if it exists. Use--pull=always(or--pull) to force a pull. - If your service uses
build:, Compose can build the image. Use--buildto force a rebuild when you changed Dockerfile/app code. - If you have both
build:andimage:, theimage:value becomes the tag for the built image. In practice: building wins, pulling doesn’t make sense for that service.
A minimal compose.yml we’ll use in examples
Use one concrete file while reading. It makes the flags and failure modes much easier to reason about.
services:
web:
build: .
image: myapp-web:dev
ports:
- "8080:8080"
env_file:
- .env
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
Code language: YAML (yaml)
The flags you’ll actually use (and what they change)
Some flags change behavior (pull/build/recreate), not just output. Here are the ones that matter in practice.
| Flag | What it changes | When to use it |
|---|---|---|
-d | Detaches and stops streaming logs | When you want the stack in the background |
--build | Forces rebuild for build: services | After Dockerfile/app changes |
--pull=always | Forces pulling newer images for image: services | When you expect a new tag/digest in registry |
--force-recreate | Recreates containers even if config seems unchanged | When you suspect sticky container state |
--no-recreate | Prevents recreating existing containers | When you want to start what exists, as-is |
--remove-orphans | Removes containers not defined in current file | After renaming/removing a service |
Bridge note: --build and --pull solve different problems. If your service is built locally, pulling won’t help. If your service is pulled from a registry, rebuilding won’t help.
Running only one service: docker compose up SERVICE
You don’t always want the whole stack. You can start a single service by name. Just remember: dependencies may still start depending on your Compose file and version.
docker compose up webCode language: Bash (bash)
Common failures and quick fixes
This section is intentionally runbook-style: fix the symptom first, then learn the ‘why’ so it doesn’t bite you again.
Port is already allocated
This happens when your host port is already in use (or a previous container still owns it). Find the process, then either stop it or change the mapping.
lsof -i :8080
# or on Linux
ss -tlnp | grep 8080Code language: Bash (bash)
Containers keep restarting / service exits immediately
A restart loop is usually just your app process exiting quickly. Compose is doing its job: keeping the service running. Your job is to inspect the real error.
docker compose logs web --tail=200
docker compose psCode language: Bash (bash)
depends_on doesn’t guarantee readiness
depends_on controls start order, not whether the dependency is actually ready. If your app boots faster than Postgres, you’ll see connection errors and restarts. Use healthchecks (as in the example file) or add retry logic in the app.
Compose can’t find environment variables
Common causes: wrong working directory, missing .env, or expecting shell exports to be present in non-interactive shells/CI. If you want predictable behavior, prefer env_file: or explicitly pass --env-file (where supported).
.env vs env_file vs environment (why variables look “blank”)
Compose has three different mechanisms that people mix up:
.env: used by Compose for variable substitution in the YAML (e.g.${TAG}).env_file:: loads variables into the container runtime environment.environment:: sets/overrides container environment variables.
If you see warnings like “variable is not set and is defaulting to a blank string”, it usually means Compose couldn’t find the variable for YAML substitution (often because .env isn’t in the working directory you think it is).
Pull access denied / image not found
If you pull from a private registry, authenticate first. Also check that the image tag exists. The error is almost never ‘Compose is broken’.
What docker compose up does not do
- It does not automatically pull newer images if a local image already exists (unless you use
--pull). - It does not automatically rebuild your image for
build:services (unless you use--build). - It does not clean up old images/volumes. That’s a separate cleanup workflow.
Orphan containers: what they are and how to clean up safely
Compose identifies a project by directory + project name. If you rename services, change directories, or run with different project names, you can end up with “orphan” containers (leftovers from an older config).
Safe sequence: start with docker compose ps -a to see what belongs to the current project. Only then consider docker compose up --remove-orphans. If you’re unsure about scope, explicitly set the project name with -p.
If youre skimming: this section is the one youll actually run.
FAQ
Does `docker compose up` rebuild images automatically?
Not reliably. If a service uses `build:`, use `docker compose up –build` after code/Dockerfile changes. If it uses `image:` only, rebuilding won’t help — you need `–pull` (and usually a new tag/digest).
What’s the difference between `docker compose up` and `docker compose start`?
`start` only starts existing containers. `up` creates and updates resources (networks/containers) when needed, so it’s the command you want after changing `compose.yml`.
When do I need `–force-recreate`?
When you suspect the container config is out of sync or you want a clean container process without changing the file. It recreates containers, but doesn’t delete named volumes.
Why does `docker compose up -d` show no logs?
Because detached mode stops log streaming. Use `docker compose logs -f` to follow logs afterwards.
How do I stop everything cleanly: `stop` vs `down`?
`stop` stops containers but keeps them around. `down` removes containers and default networks. Add `-v` only if you also want to remove volumes (data loss risk).







Leave a Reply