What this error means
Docker Compose requires every service to specify how its container image is obtained. Each service must define one of:
- image: pull an existing image by name/tag
- build: build an image from a local context (and optional Dockerfile)
If neither is present, Compose fails with: "Service declares neither an image nor a build context" (or similar wording).
Minimal working example
The file below shows both ways to satisfy the requirement.
# compose.yaml
services:
api:
build:
context: .
dockerfile: ./Dockerfile
image: myorg/api:dev # optional but recommended to tag the built image
ports:
- "8080:8080"
redis:
image: redis:7.2-alpine
ports:
- "6379:6379"
Dockerfile for api:
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
Run:
docker compose up --build
Quickstart: fix the error
- Identify offending services
- Run:
docker compose configto see the fully rendered file. - Look for services missing both
imageandbuild.
- Decide: image or build
- If you have a published image: add
image: name:tag. - If you need to build locally: add
build:with at leastcontext:.
- Add the minimal required fields
services:
svc_from_image:
image: myorg/svc:1.2.3
svc_from_source:
build:
context: ./svc
dockerfile: Dockerfile
- Validate
docker compose config(checks structure and interpolation)- Optional:
docker compose config --dry-runto skim output without running
- Rebuild and run
docker compose up --build --detach
When to use image vs build
| Choose | When |
|---|---|
| image | You pull from a registry; reproducible deployments; CI prebuilds images. |
| build | You have source and Dockerfile; local dev; need to pass build args or targets. |
Note: You may specify both build and image. Compose will build and tag the result as the image name, which helps with caching and docker compose push.
Common causes and how to fix
Missing field entirely
- Symptom: service lists only env/ports but no
imageorbuild. - Fix: add either
imageorbuild.context.
- Symptom: service lists only env/ports but no
Empty or mis-resolved variables
- Example:
image: ${IMAGE_TAG}whereIMAGE_TAGis unset or empty. - Fix: define the variable (.env or environment), or provide a default:
image: myorg/app:${IMAGE_TAG:-dev}. Forbuild.context, ensure the path variable resolves to a valid directory. - Tip:
docker compose config --no-interpolatecan help spot placeholders.
- Example:
YAML indentation or typos
buildnested under the wrong key, orbuilder:instead ofbuild:.- Fix: validate YAML and ensure
buildis at the service level, not underenvironmentordeploy.
Wrong file loaded
- You edited
compose.yamlbut invoked Compose in another directory, or used different-ffiles in CI. - Fix: run with explicit files:
docker compose -f compose.yaml -f compose.override.yaml up.
- You edited
Overrides or profiles removing fields
- An override file may replace a service without its
image/build. - Fix: inspect the merged config via
docker compose -f base.yaml -f override.yaml config.
- An override file may replace a service without its
Reusable anchors/extends not applied
- If you use YAML anchors or
extends, ensure the target includesimageorbuildand the reference is correct.
- If you use YAML anchors or
Validate your Compose configuration
- Render the final configuration:
docker compose config
- Show with profiles enabled if you use them:
docker compose --profile dev config
- Lint YAML (optional but useful):
yamllint compose.yaml
Advanced build configuration
If you build locally, specify a context directory; optional fields fine-tune the build.
services:
web:
build:
context: ./web
dockerfile: Dockerfile
target: prod
args:
APP_ENV: production
ssh:
- default
cache_from:
- type=registry,ref=myorg/web:cache
cache_to:
- type=inline
image: myorg/web:1.0.0
Notes:
- context: directory sent to the builder; keep it small with .dockerignore.
- dockerfile: relative to context.
- target: for multi-stage Dockerfiles.
- args/ssh/cache_*: require BuildKit (enabled by default in modern Docker).
Performance notes
- Prefer prebuilt images in CI for faster deployments; use
image:anddocker compose pull. - For local dev, keep contexts minimal and use
.dockerignoreto reduce transfers. - Tag built images via
image:to maximize cache reuse across runs and services. - Use multi-stage Dockerfiles to keep runtime images small and builds fast.
- Rebuild selectively:
docker compose build service_nameinstead of rebuilding all. - Leverage BuildKit cache exports if CI runners are ephemeral.
Pitfalls
- Setting
build: .at repo root without a proper.dockerignorecan balloon build time. - Providing both
imageandbuildwith a mutable tag likelatestcan cause ambiguity in CI; prefer immutable tags (commit SHA). - Using Windows path separators in
build.contextcan break on Linux runners; use POSIX-style paths. - Relying on Compose defaults for the Dockerfile name while it lives elsewhere; explicitly set
dockerfile:.
Tiny FAQ
Can I specify both image and build? Yes. Compose will build and tag the image with the
imagename. This is common for development and forcompose pushflows.Does every service need one of them, even if it only mounts volumes? Yes. Volumes, networks, and environment do not replace the requirement for an image source.
Why do I still get the error after adding build? Ensure
build.contextpoints to an existing directory and that YAML indentation is correct. Re-check withdocker compose config.How do I check which file introduced the problem? Render with all
-ffiles you pass to Compose and examine the merged output. Remove files one by one to isolate.Does profiles affect validation? Some tooling validates the composed file prior to profile filtering. To be safe, ensure every defined service has
imageorbuild.
Summary
Add either image or build.context for every service, validate with docker compose config, and keep build contexts lean. This resolves the error and improves your workflow.