KhueApps
Home/DevOps/Fix 'Service declares neither an image nor a build context' in Compose

Fix 'Service declares neither an image nor a build context' in Compose

Last updated: October 07, 2025

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

  1. Identify offending services
  • Run: docker compose config to see the fully rendered file.
  • Look for services missing both image and build.
  1. Decide: image or build
  • If you have a published image: add image: name:tag.
  • If you need to build locally: add build: with at least context:.
  1. Add the minimal required fields
services:
  svc_from_image:
    image: myorg/svc:1.2.3

  svc_from_source:
    build:
      context: ./svc
      dockerfile: Dockerfile
  1. Validate
  • docker compose config (checks structure and interpolation)
  • Optional: docker compose config --dry-run to skim output without running
  1. Rebuild and run
  • docker compose up --build --detach

When to use image vs build

ChooseWhen
imageYou pull from a registry; reproducible deployments; CI prebuilds images.
buildYou 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 image or build.
    • Fix: add either image or build.context.
  • Empty or mis-resolved variables

    • Example: image: ${IMAGE_TAG} where IMAGE_TAG is unset or empty.
    • Fix: define the variable (.env or environment), or provide a default: image: myorg/app:${IMAGE_TAG:-dev}. For build.context, ensure the path variable resolves to a valid directory.
    • Tip: docker compose config --no-interpolate can help spot placeholders.
  • YAML indentation or typos

    • build nested under the wrong key, or builder: instead of build:.
    • Fix: validate YAML and ensure build is at the service level, not under environment or deploy.
  • Wrong file loaded

    • You edited compose.yaml but invoked Compose in another directory, or used different -f files in CI.
    • Fix: run with explicit files: docker compose -f compose.yaml -f compose.override.yaml up.
  • 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.
  • Reusable anchors/extends not applied

    • If you use YAML anchors or extends, ensure the target includes image or build and the reference is correct.

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: and docker compose pull.
  • For local dev, keep contexts minimal and use .dockerignore to 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_name instead of rebuilding all.
  • Leverage BuildKit cache exports if CI runners are ephemeral.

Pitfalls

  • Setting build: . at repo root without a proper .dockerignore can balloon build time.
  • Providing both image and build with a mutable tag like latest can cause ambiguity in CI; prefer immutable tags (commit SHA).
  • Using Windows path separators in build.context can 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 image name. This is common for development and for compose push flows.

  • 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.context points to an existing directory and that YAML indentation is correct. Re-check with docker compose config.

  • How do I check which file introduced the problem? Render with all -f files 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 image or build.

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.

Series: Docker

DevOps