Skip to content

The Shipping Manifest Log: Docker Compose

Command / KeyPurposeExample
services:The root YAML key defining the services in the stack.services:
build: .Tells Compose to build a custom image from the local Dockerfile.build: .
image:Tells Compose to use a pre-built image.image: mongo:8.0
ports:Publishes a container port to the host machine.- "3000:3000"
environment:Passes environment variables into a container.- MONGO_URI=${MONGO_URI}
volumes:Defines persistent storage or file mounts.- mongo_data:/data/db
docker exec -itRuns an interactive command inside a running container.docker exec -it db_service mongosh
docker cpCopies files between our machine and a container.docker cp projects.json db_service:/projects.json
docker compose execRuns a command inside a running Compose-managed service.docker compose exec db_service mongosh
docker compose upStarts the stack and attaches the logs to our terminal.docker compose up
docker compose up -dStarts the stack in the background.docker compose up -d
docker compose up --buildRebuilds images as needed before starting the stack.docker compose up --build
docker compose downStops and removes containers and the project network.docker compose down
docker compose psShows the status of the services in the stack.docker compose ps
docker compose logs -fTails the live log output from the stack.docker compose logs -f
docker compose execRuns a command inside a running service container.docker compose exec db_service mongosh

  • The compose.yaml File: This is the single source of truth for the local stack. It lives in the project root and describes the services, ports, volumes, and config.
  • Service Discovery: Inside the Compose network, services can usually reach each other by service name. That is why db_service works as the MongoDB hostname.
  • The .env Layer: Compose automatically looks for a .env file beside compose.yaml and can use it for variable interpolation like ${MONGO_URI}.
  • Persistent Storage: A named volume such as mongo_data lets MongoDB keep its data even if the container is removed and recreated.

ProblemLikely CauseQuick Fix
ECONNREFUSED using localhostThe API is looking for MongoDB inside its own container.Change the hostname to db_service.
Code changes are not showing upThe running container is still based on an older image.Run docker compose up --build.
”Port is already allocated”Something else on the host is already using that port.Check docker ps or stop the conflicting local process.
Data disappears after teardownNo named volume was attached, or it was removed.Add mongo_data:/data/db and avoid docker compose down -v.
Container is running but app is sadThe service started, but config or connectivity is wrong.Check docker compose logs -f and verify environment values.

  • Internal vs External: The API needs port publishing so our browser can reach it. MongoDB does not need a published host port just for the API to connect internally.
  • localhost Means Self: Inside a container, localhost points back to that same container, not to the host machine and not to another service.
  • Volumes Are About State: Containers are disposable. Database data is not. That is why stateful services usually need a named volume.
  • Compose Is Declarative: Change the compose.yaml file when the setup needs to change. Do not rely on a growing pile of one-off manual commands.
Nice Mental Model

Our browser reaches the API through localhost:3000.

The API reaches MongoDB through db_service:27017.

Same stack. Different paths.

Careful with `down -v`

A plain docker compose down removes containers and the project network.

A docker compose down -v also removes named volumes, which means database data can be deleted too.


Official Compose Documentation

Multi-Container Docker Compose Study Guide

Complete Docker Compose Demo GitHub Repo

We’ve got local orchestration under control. Next, we’ll start looking at what it takes to move from a clean local stack toward an actual deployment workflow.