skip to content

In Docker Compose, how do `-p` (a second stack copy) and `up --scale` (more replicas) differ, and what collides in each?

level: middleimportance: should knowfreq 28%

answer

  1. a whole stack versus one service
  2. the name prefixes every resource
  3. later commands need the same name
  4. fixed host ports and container_name

basics

~20 s

-p starts a separate project whose containers, network and named volumes all carry the new name; --scale adds replicas of one service inside the same project. Both still collide on fixed host ports and on a fixed container_name.

solid answer

~40 s

`docker compose -p shop-feature up -d` starts a **separate project**: containers named `shop-feature-api-1`, a default network `shop-feature_default` and named volumes such as `shop-feature_pgdata`, so the copy gets its own empty database. Every later command for it (`ps`, `logs`, `exec`, `down`) must carry the same `-p`, or it acts on the other project. `docker compose up -d --scale api=3` stays in **one project** and runs `api-1` to `api-3` beside a single `db` and `redis`, overriding any `scale:` in the file. What still clashes is whatever the host owns: a fixed host port such as `"8080:8080"` binds only once, so the second stack or the second replica fails; a `container_name` is rejected outright when scale exceeds 1 and collides across projects; and bind-mounted host paths are shared by every copy.

code

bash · 9 lines
bash
# an isolated second copy of the stack for a feature branch
docker compose -p shop-feature up -d
docker compose -p shop-feature logs -f --tail 20 api
docker compose -p shop-feature down

# three api replicas in the default project
docker compose up -d --scale api=3
docker compose exec --index 2 api sh
docker compose port --index 2 api 8080

go deeper

for a junior

Recall that -p names the project and that every container, network and named volume carries that name, while --scale SERVICE=NUM adds copies of one service.

for a middle

Explain what is isolated and what is not: names, network and named volumes are per project, while host ports, container_name and bind-mounted host paths belong to the host, and later commands need the same -p.

for a senior

Show how you make a stack safe to copy and scale: container-port-only publishing or a proxy, no container_name, and a unique project name per checkout or CI job so parallel runs never touch each other.

for a principal

Weigh when copies on one host stop being worth it: shared ports, memory and disk are the ceiling, and beyond it the answer is separate hosts or an orchestrator rather than more flags.

## Two different kinds of "more" Developers ask for "another one" in two senses. Sometimes it is a **whole second stack**: another `api`, `db` and `redis` for a feature branch, with its own data. Sometimes it is **more copies of one service** in the same stack: three `api` containers to reproduce a race against one database. Compose has a tool for each, the **project name** (`-p`) and **`--scale`**, and they isolate very different things. ## What `-p` changes `-p` (`--project-name`) is a global flag, so it goes **before** the subcommand: `docker compose -p shop-feature up -d`. Compose uses the project name to namespace what it creates: 1. **Containers:** `shop-feature-api-1`, `shop-feature-db-1`. 2. **The default network:** `shop-feature_default`. 3. **Named volumes:** `shop-feature_pgdata`, so the copy starts with an **empty** database. 4. **A `com.docker.compose.project` label** on each container, which is how later commands find them. Three consequences follow: - Every later command for that copy (`ps`, `logs`, `exec`, `run`, `down`) needs the same `-p`, or it acts on the other project. - The name must be valid: lowercase letters, digits, hyphens and underscores, starting with a letter or a digit. - `docker compose ls` lists the running projects on the host, which is how you find a copy you forgot. Do not confuse it with `-p` after `run`: in `docker compose run -p 8080:80 api`, `-p` is `--publish`, a port mapping for the one-off container. ## What `--scale` changes `docker compose up -d --scale api=3` stays in one project and runs `api-1`, `api-2` and `api-3` beside a single `db` and `redis`. The flag can be repeated for several services, and it overrides a `scale:` value in the file; `docker compose scale api=3` does the same as a subcommand. With replicas, commands that target one container take `--index`, as in `docker compose exec --index 2 api sh` or `docker compose port --index 2 api 8080`, and `logs --tail N` counts lines per replica. ## What collides | Resource | Second project with `-p` | Extra replicas with `--scale` | |---|---|---| | Fixed host port `"8080:8080"` | clashes: the second stack cannot bind it | clashes: only one replica binds it | | `container_name: shop-api` | clashes: the name is already taken | rejected: scale above 1 is an error | | Named volume `pgdata` | separate, prefixed per project | the same volume in every replica | | Host bind mount `./data:/data` | the same host directory for both | the same host directory for all | | Default network | separate per project | shared, as always within one project | ## Fixing the port clash - **Publish the container port only**, as `"8080"`, and let the runtime pick a free host port; read it back with `docker compose port api 8080` or from the `PORTS` column of `docker compose ps`. - **Put a reverse proxy in front** as the only service with a fixed host port, and publish nothing on `api`. - **Give a second project its own host port** through an override file or a variable in the port mapping; both are compose-file configuration, not something `-p` does for you. - **Remove `container_name`** from any service you might ever copy or scale; Compose already names containers uniquely. ## Choosing project names in scripts A project name is the isolation boundary, so scripts that start stacks should pick it deliberately: - **One name per checkout or job.** Two automated runs on one host that share a project name share containers, and one run's `down` removes the other's stack. - **Normalise what you feed in.** A branch name such as `Feature/Login` is not a valid project name; lower-case it and replace the slash before passing it to `-p`. - **Tear down with the same name.** `docker compose -p <name> down` removes only that project's containers and network, which is what makes parallel copies safe to clean up. ## Where this stops Both techniques share one host's ports, CPU, memory and disk. They suit development and tests: a feature branch beside `main`, or a handful of replicas to reproduce a concurrency bug. Running replicated services across machines, with rolling updates and rescheduling, is an orchestrator's job rather than Compose's.

  • With `ports: - "8080"` on a scaled `api` service, how do you find the host port replica 2 received?
    Ask Compose: `docker compose port --index 2 api 8080` prints the host address and port mapped to container port 8080 on replica 2. `docker compose ps` shows the same mappings in its `PORTS` column for every replica.
  • You started a copy with `docker compose -p shop-feature up -d`, then ran plain `docker compose down` in the same folder. What happened?
    Plain `down` resolved the default project name, not `shop-feature`, so it removed the original stack and left the copy running. Commands find containers by the project label, so every command for the copy needs `-p shop-feature`; `docker compose ls` shows which projects are still running.

saying these in an interview costs you the question

  • -p only renames the containers; the network and volumes stay shared.
  • A second project started with -p avoids every conflict, fixed host ports included.
  • --scale gives each replica its own host port automatically.
  • After starting a copy with -p, plain docker compose down removes that copy.
  • In docker compose run, -p sets the project name for the one-off container.