docker/compose · error

invalid service %q. Must specify either image or build

Error message

invalid service %q. Must specify either image or build

What it means

Thrown by api.BuildOptions.Apply while mutating the project before a build: every service in the compose model must be buildable or pullable, so it must declare an image, a build section, or come from a provider (service.Provider != nil). A service with none of these cannot be built or run, so the API rejects the whole project up front. The name in the message is the service key as written in compose.yaml.

Source

Thrown at pkg/api/api.go:236

	Print bool
	// Check let builder validate build configuration
	Check bool
	// Attestations enables attestation generation
	Attestations bool
	// Provenance generate a provenance attestation
	Provenance string
	// SBOM generate a SBOM attestation
	SBOM string
	// Out is the stream to write build progress
	Out io.Writer
}

// Apply mutates project according to build options
func (o BuildOptions) Apply(project *types.Project) error {
	platform := project.Environment["DOCKER_DEFAULT_PLATFORM"]
	for name, service := range project.Services {
		if service.Provider == nil && service.Image == "" && service.Build == nil {
			return fmt.Errorf("invalid service %q. Must specify either image or build", name)
		}

		if service.Build == nil {
			continue
		}
		if platform != "" {
			if len(service.Build.Platforms) > 0 && !slices.Contains(service.Build.Platforms, platform) {
				return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", name, platform)
			}
			service.Platform = platform
		}
		if service.Platform != "" {
			if len(service.Build.Platforms) > 0 && !slices.Contains(service.Build.Platforms, service.Platform) {
				return fmt.Errorf("service %q build configuration does not support platform: %s", name, service.Platform)
			}
		}

		service.Build.Pull = service.Build.Pull || o.Pull

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Open the compose file, find the named service, and add either an image: <tag> or a build: section (e.g. build: . with a Dockerfile)
  2. If the service was meant to be provided externally, mark it with provider: so service.Provider is set and the check is skipped
  3. Check YAML indentation around the service to confirm image:/build: are nested under the correct service key
  4. Run docker compose config to let the CLI render the effective model and reveal services that lost their image/build keys

Example fix

# before (compose.yaml)
services:
  web:
    ports:
      - "8080:80"
# after
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
Defensive patterns

Strategy: validation

Validate before calling

// Run before api.BuildOptions.Apply / compose build:
func validateServices(project *types.Project) error {
	for name, svc := range project.Services {
		if svc.Provider == nil && svc.Image == "" && svc.Build == nil {
			return fmt.Errorf("service %q lacks image, build, and provider", name)
		}
	}
	return nil
}

Prevention

When it happens

Trigger: Calling api.BuildOptions.Apply(project) (directly or via the build/up API) on a project where a service defines neither image: nor build: and is not a provider-based service (e.g. a service containing only ports/env/depends_on). Also triggered by a typo'd key (e.g. images: or builds:) that compose-go silently ignores as an extension field.

Common situations: A compose.yaml service that was meant to be a config-only placeholder or was copy-pasted incompletely; indentation errors that push image:/build: under the wrong service or outside the service map; YAML typos that make the parser treat image as an unknown extension; using a service that only exists as a profile target with no runnable artifact.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/f0cd7eb573263292. Report an issue: GitHub.