Skip to content
Latchkey

Docker Compose "services.<name> must be a mapping" in CI

Compose expects each service to be a mapping of keys (image, ports, ...). services.<name> must be a mapping means the service was written as a scalar, a list, or left empty - so its body is not the key/value structure Compose requires.

What this error means

A docker compose config/up fails with services.<name> must be a mapping (or ... contains an invalid type, it should be an object). The named service has no proper key/value body.

docker
services.api must be a mapping
# services:
#   api:            <- nothing indented under it, so it parses as null
#   web:
#     image: nginx

Diagnose it: read the resolved config, not the file you wrote

Compose merges override files, interpolates variables, and applies defaults before it does anything. Most Compose failures in CI are visible in the resolved configuration and invisible in the source file, because the value you are debugging came from an unset variable that quietly became an empty string.

Terminal
# the fully merged, interpolated configuration Compose will actually run
docker compose config

# fail loudly on unset variables instead of silently interpolating empty
docker compose --env-file .env config --quiet || echo "invalid"

# which override files were picked up
docker compose config --services

Common causes

An empty service body

A service key with nothing indented beneath it parses as null, not a mapping, and Compose rejects it.

The service written as a scalar or list

Assigning a string or a YAML list to a service name instead of a key/value block fails the mapping requirement.

Indentation collapses the service body

Under-indented keys that do not nest under the service name leave the service effectively empty.

How to fix it

Give the service a proper mapping body

Indent at least one key (like image) under the service name.

docker-compose.yml
services:
  api:
    image: myorg/api:1.4.2
    ports:
      - "8080:8080"

Remove or complete placeholder services

Delete empty service stubs or fill them in.

Terminal
docker compose config   # confirms each service is a valid mapping

Bind mounts behave differently on a runner

  • A relative bind source is resolved against the compose file location, not the working directory of the shell that invoked it.
  • The host path must exist before up. Compose creates missing directories for named volumes but not for bind mounts, and the failure surfaces as a mount error rather than a missing-path error.
  • On a CI runner the workspace path differs from your machine, so any absolute host path in a compose file is a portability bug waiting for its first CI run.
  • Prefer named volumes for anything that does not genuinely need to be read from the host. They remove the whole class of problem.

How to prevent it

  • Give each service at least an image or build key.
  • Remove placeholder/empty service entries.
  • Run docker compose config to validate service structure.

Frequently asked questions

What causes Docker Compose "services.<name> must be a mapping" in CI?
There are 3 common causes: an empty service body, the service written as a scalar or list, and indentation collapses the service body. A service key with nothing indented beneath it parses as null, not a mapping, and Compose rejects it.
How do I fix Docker Compose "services.<name> must be a mapping" in CI?
There are 2 fixes depending on which cause you have: give the service a proper mapping body and remove or complete placeholder services. Work through them in order, since the first is the most common.
What does Docker Compose "services.<name> must be a mapping" in CI actually mean?
A docker compose config/up fails with services.<name> must be a mapping (or ...
How do I stop Docker Compose "services.<name> must be a mapping" in CI happening again?
Give each service at least an image or build key. 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