Docker Compose Build
Learn to build images using docker compose build for efficient multi-service development.
Focus: docker compose build images
You've built Dockerfiles by hand, run docker build a hundred times, and started containers with docker run. But as your project grows to include a database, a cache, and a background worker, those individual docker build commands become a chore: you need to remember the right context paths, tags, and dependencies for each service. docker compose build solves this by centralizing all your image building logic into a single docker-compose.yml file, letting you rebuild an entire multi-service stack with one command.
The problem this lesson solves
When developing a real-world application, you rarely have just one container. You might have a Python web API, a Redis cache, a PostgreSQL database, and a Celery worker. In a team environment, every developer needs to build the exact same images. Running docker build -t myapp-web . and docker build -t myapp-worker ./worker manually is error-prone and slow. You'll forget tags, mismatch base images, or build in the wrong order.
Docker Compose gives you a declarative way to define how each image is built. Instead of scripts or tribal knowledge, your build process lives in version control. docker compose build reads the build section of your services and executes the builds in the correct order, respecting dependencies like depends_on. This lesson will teach you to stop building images individually and start managing your builds as a unified workflow.
Core concept / mental model
Think of docker compose build as an orchestrated batch builder. Where docker build is a single hammer, docker compose build is a toolbox that contains many hammers and knows which one to use first.
What docker compose build does
- Reads your
docker-compose.yml(orcompose.yaml) file. - For each service that has a
buildkey, it runsdocker buildusing the specified context, Dockerfile, and build arguments. - It builds images in the order defined by
depends_onwhere possible, ensuring base images are ready before dependent services need them. - Tags images automatically using the pattern
<project>_<service>(or your custom tags).
Project name is key
By default, Compose uses the directory name as the project name. So if your project folder is myapp, images will be named myapp_web, myapp_db, and so on. You can override this with -p flag or a name top-level key in your Compose file.
Pro tip: Use
docker compose build --helpto see all available flags. The--no-cacheflag is your best friend when debugging stale builds.
How it works step by step
Let's break down the build process for a typical web app with a database.
1. Define services with build keys
In your docker-compose.yml, each service can have a build key instead of (or in addition to) an image key. The build key points to a directory containing a Dockerfile.
services:
web:
build: ./web
ports:
- "8000:8000"
db:
build: ./db
environment:
POSTGRES_PASSWORD: example
2. Run docker compose build
$ docker compose build
[+] Building 3.4s (12/12) FINISHED
=> [web internal] load build definition from Dockerfile 0.0s
=> [web internal] transfering dockerfile: 120B 0.0s
=> [web] resolve docker.io/library/python:3.11-slim 0.2s
=> [web] CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"] 0.1s
=> [db internal] load build definition from Dockerfile 0.0s
=> [db] FROM postgres:15-alpine 0.5s
...
Compose builds both images, tagging them automatically. You can see the built images with docker images:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp_web latest abc123def456 2 minutes ago 150MB
myapp_db latest def789abc012 2 minutes ago 200MB
Note: If a service has both
buildandimagekeys, theimagetag is applied after building. This is useful for pushing to a registry.
Hands-on walkthrough
Let's build a practical example: a Python web app with a Redis cache.
Project structure
myapp/
├── docker-compose.yml
├── web/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app.py
└── redis/
└── Dockerfile
docker-compose.yml
version: "3.9"
services:
web:
build: ./web
ports:
- "5000:5000"
depends_on:
- cache
cache:
build: ./redis
web/Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Build and run
$ cd myapp
$ docker compose build
$ docker compose up -d
This builds both images, then starts containers. Notice Compose built cache first because web depends on it.
Build with arguments
Sometimes you need to pass build-time variables, like a version number:
services:
web:
build:
context: ./web
args:
APP_VERSION: "1.0.0"
Build with a specific argument:
$ docker compose build --build-arg APP_VERSION=2.0.0
Pro tip: Use
.envfiles to keep build arguments out of your Compose file. Create a.envfile withAPP_VERSION=1.5.0and reference it as${APP_VERSION:-latest}in your Compose file.
Compare options / when to choose what
| Feature | docker build |
docker compose build |
|---|---|---|
| Single image | ✅ Best | ❌ Overkill |
| Multi-service app | ❌ Manual orchestration | ✅ Best |
| CI/CD pipeline | ✅ Good for single service | ✅ Good for full stack |
| Development workflow | ❌ Repetitive | ✅ One command rebuild |
| Custom network builds | ❌ Manual network config | ✅ Automatic |
| Layer caching across builds | ✅ Same as docker build |
✅ Same, plus shared cache |
When to use each:
- Use docker build for standalone images (e.g., a base image or a CLI tool).
- Use docker compose build when your application involves multiple interconnected services.
Troubleshooting & edge cases
Dockerfile not found
$ docker compose build
service "web" failed to build: unable to prepare context: path "./web" not found
Fix: Ensure the build path is relative to the location of your Compose file, not your current working directory.
Unsupported Dockerfile name
If your Dockerfile isn't named Dockerfile, specify the filename:
services:
web:
build:
context: ./web
dockerfile: Dockerfile.dev
Build takes forever / no caching
If builds seem slow or don't use cache, ensure your .dockerignore file excludes unnecessary files (like node_modules, __pycache__, .git). Also check that files aren't changing on every build (e.g., timestamps in copied files).
Build succeeds but containers crash immediately
This is usually a runtime issue, not a build issue. Debug with:
$ docker compose logs
$ docker compose run web sh # enter the container interactively
What you learned & what's next
In this lesson, you learned to: - Explain the core idea behind Build images with docker compose build. - Complete a practical exercise for Build images with docker compose build, including passing build arguments and understanding build order. - Connect Build images with docker compose build to the next lesson in the track.
You now know how to replace a dozen docker build commands with a single docker compose build call, saving time and reducing errors. Your team can share a single Compose file and rebuild the entire stack consistently.
What's next: The next lesson covers Using volumes with Docker Compose to persist data across container restarts. You'll learn how to add volumes to your Compose services, ensuring your database data survives rebuilds.
Practice recap
Create a new project folder with a docker-compose.yml that defines two services: app (a simple Python Flask app) and redis. Add a build key for each service pointing to subdirectories containing Dockerfiles. Run docker compose build and verify the images exist with docker images. Then add a build argument for APP_VERSION and rebuild with a different version.
Common mistakes
- Forgetting to add a
dockerignorefile, causing the build context to include gigabytes of unnecessary files (likenode_modules) and making builds extremely slow. - Expecting
docker compose buildto rebuild images when only the Compose file changes — it only rebuilds when the build context or Dockerfile changes. - Using
buildandimagekeys together without understanding thatimagebecomes a tag after build, not a pull from a registry. - Assuming
depends_onguarantees build order — it only guarantees container startup order. Usedepends_onwithcondition: service_completed_successfullyfor strict build dependencies.
Variations
- Use
docker compose build --parallelto build multiple services simultaneously when they have no interdependencies, reducing overall build time. - Use
docker compose up --buildto combine build and start in one command — useful for rapid iteration during development. - Use
docker compose build --pushto build and push images to a registry in one step, ideal for CI/CD pipelines.
Real-world use cases
- A team of 5 developers uses
docker compose buildto ensure everyone builds exactly the same web API, worker, and database images from a shared Compose file. - A CI pipeline runs
docker compose build --pull --no-cacheto build production-ready images from scratch, then runs integration tests withdocker compose up. - A Python ML project uses
docker compose buildto build a Jupyter notebook server and a GPU-accelerated training service, with custom build args for CUDA versions.
Key takeaways
docker compose buildbuilds all images defined in your Compose file with a single command, respecting build order and dependencies.- Images are tagged automatically with the pattern
<project>_<service>, but you can override with custom tags using theimagekey. - Use
--no-cacheand--pullflags for fresh builds in CI/CD pipelines or when debugging stale layers. - Always include a
.dockerignorefile to keep build contexts small and builds fast. - Combine
docker compose buildwithdocker compose up --buildfor a seamless build-and-run workflow in development.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.