docker compose up and down
docker compose up starts the services in a Compose project, and docker compose down tears that project back down. These two commands are the daily start and stop controls for multi-container Docker applications. Knowing exactly what they create, reuse, stop, and delete is what keeps local development fast and prevents accidental data loss.
Overview: How it works
A Compose file describes an application as services, networks, volumes, build instructions, published ports, and environment. A service is usually one container template: for example, a web app service and a database service. When you run docker compose up, the Compose plugin reads compose.yml or docker-compose.yml, calculates the project name, creates the project network and named volumes if needed, builds or pulls images, then creates and starts containers for the services.
Compose is not a separate container runtime. It talks to the same Docker Engine API used by docker run. The Engine still manages images as read-only layered templates, and each container still gets a thin writable layer plus a running process. Compose adds project-level coordination: containers receive labels that record the project and service they belong to, and they join a default bridge network where service names become DNS names. That is why a container named by the api service can connect to a database using host name db.
By default, the project name comes from the directory name. This matters because Compose names resources with that project prefix. A directory called shop might produce containers like shop-api-1, a network like shop_default, and a volume like shop_pgdata. You can override the project name with -p or the COMPOSE_PROJECT_NAME environment variable when you need two copies of the same stack running side by side.
docker compose down reverses the project-level work. It stops running service containers, removes those containers, and removes networks created by Compose. It does not remove named volumes by default, because volumes often contain important persistent data. It also does not remove images by default. This difference is intentional: stopping the application should not silently erase the database or force every image to be rebuilt.
Syntax
docker compose [OPTIONS] up [OPTIONS] [SERVICE...]
docker compose [OPTIONS] down [OPTIONS]
The first [OPTIONS] position contains global Compose options such as -f for an alternate file and -p for a project name. The second options position belongs to the specific command.
| Option | Command | Meaning |
|---|---|---|
-f FILE |
global | Use a specific Compose file instead of the default discovery names. |
-p NAME |
global | Set the Compose project name, which affects container, network, and volume names. |
-d |
up |
Start containers in detached mode and return the terminal prompt. |
--build |
up |
Build service images before starting containers. |
--force-recreate |
up |
Recreate containers even if their configuration and image have not changed. |
--remove-orphans |
up or down |
Remove containers from the same project that are no longer defined in the current file. |
--volumes |
down |
Also remove named volumes declared by the Compose file and anonymous volumes attached to containers. |
--rmi local |
down |
Remove images that do not have a custom tag. |
Examples
Start a simple web service
Create this compose.yml in an empty directory:
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
Then start it in the foreground:
docker compose up
Output:
[+] Running 2/2
✔ Network demo_default Created
✔ Container demo-web-1 Created
Attaching to web-1
web-1 | /docker-entrypoint.sh: Configuration complete; ready for start up
Compose pulled the pinned Nginx image if it was missing, created a project network, created the container, published host port 8080 to container port 80, and attached your terminal to the logs. Pressing Ctrl+C stops the containers for this foreground session, but it does not remove the created container or network. Run docker compose down when you want the project resources removed.
Run a web app and database in the background
A more realistic development stack often has a stateless app and a stateful database:
services:
api:
image: node:20-alpine
working_dir: /app
command: ["node", "server.js"]
volumes:
- ./app:/app
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: changeme
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
docker compose up -d
docker compose ps
Output:
[+] Running 4/4
✔ Network api_default Created
✔ Volume api_pgdata Created
✔ Container api-db-1 Started
✔ Container api-api-1 Started
NAME IMAGE COMMAND SERVICE STATUS PORTS
api-api-1 node:20-alpine "docker-entrypoint.s…" api Up 3 seconds 0.0.0.0:3000->3000/tcp
api-db-1 postgres:16-alpine "docker-entrypoint.s…" db Up 4 seconds 5432/tcp
The -d flag starts the project in detached mode, so logs keep running in Docker while your shell returns. The api service can reach PostgreSQL at host name db on the internal Compose network. The database stores files in the named volume pgdata, which Docker prefixes with the project name. The bind mount ./app:/app maps your source directory into the Node container for local development.
Stop and remove the project
docker compose down
Output:
[+] Running 3/3
✔ Container api-api-1 Removed
✔ Container api-db-1 Removed
✔ Network api_default Removed
This removes the service containers and Compose-created network. It intentionally leaves api_pgdata in place. If you later run docker compose up -d again in the same project, PostgreSQL will reuse the existing volume and the database contents will still be there.
Reset everything, including volumes
docker compose down --volumes --remove-orphans
Output:
[+] Running 4/4
✔ Container api-api-1 Removed
✔ Container api-db-1 Removed
✔ Volume api_pgdata Removed
✔ Network api_default Removed
Use this when you want a clean local reset, such as rebuilding a seed database from scratch. Do not use --volumes casually against a project that contains data you care about. It removes named volumes declared in the Compose file for that project.
How it works step by step
- Compose loads and merges configuration from the selected Compose file or files. It resolves variables, service definitions, networks, volumes, and profiles.
- It chooses a project name. This name is used in Docker labels and generated resource names, allowing Compose to find the same project later.
- For each service, Compose checks whether an image must be built, pulled, or reused. If a service has
build:,docker compose up --buildasks Docker to build the image before starting. - Compose creates missing named volumes and networks. The default network gives services automatic DNS entries matching their service names.
- Compose creates or reuses containers. If the service configuration changed, Compose recreates the affected container so the new settings take effect.
- Compose starts containers in dependency order as much as possible.
depends_oncontrols start order, but it does not prove that a database is ready to accept connections unless you add health checks and application retry logic. - With foreground
up, Compose attaches log streams to your terminal. Withup -d, containers keep running in the background. downfinds containers and networks with the project labels, stops the containers, removes them, and removes project networks. Volumes and images stay unless you request extra cleanup.
Common Mistakes
Expecting Ctrl+C to be the same as down
docker compose up
# Press Ctrl+C and assume everything was deleted
This stops a foreground run, but it is not a full cleanup. Containers may remain in an exited state, and networks can remain. The fix is to run:
docker compose down
Deleting database data by using –volumes automatically
docker compose down --volumes
This is useful for a reset, but wrong as a default shutdown command for a stateful stack. It removes project volumes such as PostgreSQL data directories. Use plain docker compose down for normal cleanup, and reserve --volumes for deliberate resets.
Changing project names without realizing volumes change too
docker compose -p api_a up -d
docker compose -p api_b up -d
These are two different projects. They get different containers, networks, and project-prefixed volumes. That is useful for parallel environments, but it surprises people who expect the second command to reuse the first project database. Use a stable project name when you need stable resource names.
Assuming depends_on waits for readiness
depends_on starts one service before another, but a just-started database may still be initializing. Production-grade apps should retry connections, and development stacks can add health checks when startup order needs to be more explicit.
Best Practices
- Use
docker compose up -dfor normal background development, then inspect withdocker compose psanddocker compose logs. - Use plain
docker compose downfor routine cleanup so named volumes survive. - Use
docker compose down --volumesonly when you intentionally want to delete project data. - Pin image tags such as
postgres:16-alpineinstead of usinglatest, becauselatestmoves over time and hurts reproducibility. - Set a stable project name with
-porCOMPOSE_PROJECT_NAMEin scripts and CI jobs. - Keep state in named volumes and source code in bind mounts. Volumes are Docker-managed storage; bind mounts point to a specific host path.
- Run
docker compose up --buildafter changing a Dockerfile or build context when you want to force a fresh image build before start. - Use
--remove-orphansafter deleting or renaming services so old containers from the same project do not keep running unnoticed.
Practice Exercises
- Create a Compose file with
nginx:1.27-alpinepublished on host port8088. Start it withup -d, list it withps, then remove it withdown. Hint: the service only needsimageandports. - Add a Redis service using
redis:7-alpineand a named volume mounted at/data. Start it, bring it down without--volumes, then start it again. Expected end state: the same named volume still exists. - Run the same Compose file twice with two different project names. Compare the generated container and network names. Hint: use
docker compose -p first up -dand a second project name in another command.
Summary
docker compose upcreates or reuses the containers, networks, volumes, and images needed by a Compose project.docker compose up -dis the standard way to run a stack in the background.docker compose downremoves service containers and Compose networks, but keeps named volumes and images by default.docker compose down --volumesdeletes project volumes and should be treated as a data reset.- Project names matter because Compose uses them to label and name resources.
- Compose coordinates Docker Engine resources; containers still use normal Docker images, writable layers, networks, and volumes underneath.
