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.
services.api.build must be a string or object
# offending:
# build:
# - context: ./api # a list, not a mappingDiagnose 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.
# 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.
services:
api:
build: ./api # string form
web:
build: # mapping form
context: ./web
dockerfile: DockerfileValidate the compose config
Render the resolved config to catch the type error before building.
docker compose config # prints the parsed config or the validation errorKeep the build context small and deterministic
- A missing
.dockerignoresendsnode_modules,.git, and build output to the daemon, which is slow and can change layer hashes between environments. - A
COPYof 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
buildxand QEMU set up explicitly; a plaindocker buildon an ARM runner silently produces an ARM image.
How to prevent it
- Run
docker compose configin CI to validate before building. - Keep service keys consistently indented; avoid stray
-under mappings. - Lint compose files with a YAML schema-aware linter.