nektos/act · error

service container failed to start

Error message

service container failed to start

What it means

A service container did not report a 'healthy' state in time. act polls the container's Docker healthcheck with an exponential backoff (delay doubling up to 10s, capped at ~30 iterations starting from HealthStarting); if the final observed state is anything other than healthy (still starting, unhealthy, or no healthcheck defined), this error is returned after waitForServiceContainer completes.

Source

Thrown at pkg/runner/run_context.go:606

		sctx, cancel := context.WithTimeout(ctx, time.Minute*5)
		defer cancel()
		health := container.HealthStarting
		delay := time.Second
		for i := 0; ; i++ {
			health = c.GetHealth(sctx)
			if health != container.HealthStarting || i > 30 {
				break
			}
			time.Sleep(delay)
			delay *= 2
			if delay > 10*time.Second {
				delay = 10 * time.Second
			}
		}
		if health == container.HealthHealthy {
			return nil
		}
		return fmt.Errorf("service container failed to start")
	}
}

func (rc *RunContext) waitForServiceContainers() common.Executor {
	return func(ctx context.Context) error {
		execs := []common.Executor{}
		for _, c := range rc.ServiceContainers {
			execs = append(execs, rc.waitForServiceContainer(c))
		}
		return common.NewParallelExecutor(len(execs), execs...)(ctx)
	}
}

func (rc *RunContext) stopServiceContainers() common.Executor {
	return func(ctx context.Context) error {
		execs := []common.Executor{}
		for _, c := range rc.ServiceContainers {
			execs = append(execs, c.Remove().Finally(c.Close()))

View on GitHub (pinned to 4f41128141)

Solutions

  1. Run `docker ps` and `docker inspect --format '{{json .State.Health}}' <service-container>` to see the healthcheck status and its last output/error.
  2. Fix whatever makes the healthcheck fail: correct credentials in the healthcheck command, exposed port, or required env vars in the service definition.
  3. Use an official image variant that ships a working HEALTHCHECK (e.g. postgres with POSTGRES_* env set properly).
  4. If the service is just slow, reduce startup work (disable fsync for test DBs) or increase act's available time rather than switching to a healthcheck-less image.
  5. If you control the image, add a Docker HEALTHCHECK so the state can transition to healthy.

Example fix

# before
services:
  postgres:
    image: postgres:16
    env: { POSTGRES_PASSWORD: '' }   # healthcheck pg_isready fails
# after
services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_PASSWORD: test
      POSTGRES_USER: test
      POSTGRES_DB: test
Defensive patterns

Strategy: retry

Validate before calling

# Pre-flight: does the image have a healthcheck?
docker inspect --format '{{.Config.Healthcheck}}' mydb:latest 2>/dev/null | grep -q CMD && echo ok || echo 'no healthcheck — service will never be healthy'

Try / catch

if err := rc.waitForServiceContainers()(ctx); err != nil {
  log.WithError(err).Warn("service not healthy; retrying once after daemon warmup")
  time.Sleep(30 * time.Second)
  err = rc.waitForServiceContainers()(ctx)
}

Prevention

When it happens

Trigger: The service image has no HEALTHCHECK and never transitions past HealthStarting, the container's healthcheck command keeps failing (DB not ready, wrong credentials, missing dependency), the image reports 'unhealthy', or the ~minutes-long polling budget expires before the service becomes ready.

Common situations: Database service containers (postgres/mysql/redis) whose healthcheck uses wrong credentials or a long startup time; custom Docker images without a HEALTHCHECK instruction; slow machines (CI runners, cold Docker daemon) where startup exceeds the wait budget; service misconfigured via options/env so the process inside crashes at boot.

Related errors


AI-assisted analysis of nektos/act@4f41128141 (2026-08-15). Data as JSON: /api/errors/b39921ede688a51a. Report an issue: GitHub.