Docker Compose — Image vs Build

2 minute read

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:

  1. Build from a Dockerfile
  2. 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