Docker Compose — Image vs Build
Services can use a pre-built image from a registry or build a custom image from a Dockerfile. This page explains both approaches and when to use each.
Using image:
Pull and run a pre-built image:
services:
db:
image: postgres:16-alpine
cache:
image: redis:7-alpine
web:
image: nginx:alpine
Use image: when:
- Using official images (databases, caches, web servers)
- Using pre-built application images from a registry
- No customization needed
Using build:
Build an image from a Dockerfile:
services:
api:
build: . # Build from Dockerfile in current directory
ports:
- "3000:3000"
The simplest form points to a directory containing a Dockerfile:
my-project/
├── compose.yml
├── Dockerfile ← build: . uses this
└── src/
└── app.js
Specifying Build Context and Dockerfile
services:
api:
build:
context: ./backend # Build context directory
dockerfile: Dockerfile.prod # Custom Dockerfile name
my-project/
├── compose.yml
└── backend/
├── Dockerfile.prod ← build uses this
└── src/
Using Both image: and build:
When you specify both, Compose builds the image and tags it with the given name:
services:
api:
build: ./backend
image: myregistry.com/myapp-api:latest
This is useful when you want to:
- Build from a Dockerfile
- Tag the result for pushing to a registry
# Build and tag
docker compose build
# Push to registry
docker compose push api
When to Use Each
┌─────────────────────────┐ ┌────────────────────────┐
│ image: │ │ build: │
│ │ │ │
│ • Official images │ │ • Your application │
│ • Databases │ │ • Custom images │
│ • Caches (Redis) │ │ • Modified base images│
│ • Message queues │ │ • Development builds │
│ • Reverse proxies │ │ │
│ • Third-party tools │ │ │
└─────────────────────────┘ └────────────────────────┘
| Scenario | Use | Why |
|---|---|---|
| PostgreSQL database | image: |
Official image, no customization |
| Your Node.js API | build: |
Custom code, needs Dockerfile |
| Nginx with custom config | image: + volumes |
Mount config file |
| Nginx with custom modules | build: |
Need to compile modules |
| Redis cache | image: |
Official image works perfectly |
Common Pattern: Mix Both
Most real-world compose files use a mix:
services:
# Your app — build from Dockerfile
api:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://user:pass@db:5432/app
REDIS_URL: redis://cache:6379
# Database — use official image
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: pass
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
# Cache — use official image
cache:
image: redis:7-alpine
volumes:
pgdata:
Building and Rebuilding
# Build images defined with build:
docker compose build
# Build and start
docker compose up --build
# Build without cache
docker compose build --no-cache
# Build specific service
docker compose build api
Summary
| Option | Purpose | When to Use |
|---|---|---|
image: |
Use pre-built image | Databases, caches, third-party |
build: |
Build from Dockerfile | Your application code |
build: + image: |
Build and tag | Registry push workflow |
Quick Reference
| Item | Detail |
|---|---|
| image: postgres:16 | Pull pre-built image from registry |
| build: . | Build from Dockerfile in current directory |
| build: context + dockerfile | Custom Dockerfile path |
| image: + build: | Build and tag for registry push |
| docker compose build | Build all images |
| docker compose up –build | Rebuild and start |