Skip to content
Latchkey

Docker Compose "services.X.build must be a string or object" in Builds

Compose validated the build key of a service and found the wrong YAML type. build must be either a string (a context path) or a mapping (context, dockerfile, …) - not a list or scalar of another type.

What this error means

A docker compose build or up --build fails during config validation with services.<name>.build must be a string or object, before any image is built. The file otherwise looks reasonable.

docker compose output
services.api.build must be a string or object
# offending:
#   build:
#     - context: ./api      # a list, not a mapping

Diagnose it: build context, cache, or platform?

A Dockerfile that builds locally and fails in CI usually differs in one of three ways: the build context contains different files, the layer cache is cold or poisoned, or the runner architecture does not match what the base image provides.

Terminal
# what is actually being sent as build context (dockerignore applies)
docker build --no-cache --progress=plain -t probe . 2>&1 | head -40

# what platform are you on, and what does the base image support?
docker version --format '{{.Server.Arch}}'
docker buildx imagetools inspect <base-image> | grep -i platform

# prove it is not a cache artefact
docker build --no-cache .

Common causes

build written as a list

A stray - turns the build mapping into a list (build:\n - context: ...). Compose expects either a bare path string or a key/value mapping, so a list is rejected.

Wrong indentation under the service

Misaligned indentation can make build’s children parse as something other than a mapping, or attach to the wrong service.

A scalar of the wrong shape

Setting build: to a number, boolean, or empty value (instead of a path string or mapping) fails the type check.

How to fix it

Use a valid string or mapping form

Either give a context path string, or a properly-indented mapping.

docker-compose.yml
services:
  api:
    build: ./api            # string form
  web:
    build:                  # mapping form
      context: ./web
      dockerfile: Dockerfile

Validate the compose config

Render the resolved config to catch the type error before building.

Terminal
docker compose config   # prints the parsed config or the validation error

Keep the build context small and deterministic

  • A missing .dockerignore sends node_modules, .git, and build output to the daemon, which is slow and can change layer hashes between environments.
  • A COPY of a path that exists locally but is gitignored will fail in CI, because the runner only has what the checkout produced.
  • Multi-arch builds need buildx and QEMU set up explicitly; a plain docker build on an ARM runner silently produces an ARM image.

How to prevent it

  • Run docker compose config in CI to validate before building.
  • Keep service keys consistently indented; avoid stray - under mappings.
  • Lint compose files with a YAML schema-aware linter.

Frequently asked questions

What causes Docker Compose "services.X.build must be a string or object" in builds?
There are 3 common causes: build written as a list, wrong indentation under the service, and a scalar of the wrong shape. A stray - turns the build mapping into a list (build:\n - context: ...).
How do I fix Docker Compose "services.X.build must be a string or object" in builds?
There are 2 fixes depending on which cause you have: use a valid string or mapping form and validate the compose config. Work through them in order, since the first is the most common.
What does Docker Compose "services.X.build must be a string or object" in builds actually mean?
A docker compose build or up --build fails during config validation with services.<name>.build must be a string or object, before any image is built.
How do I stop Docker Compose "services.X.build must be a string or object" in builds happening again?
Run docker compose config in CI to validate before building. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card