docker/compose · error
service %q pre_start[%d] exited with code %d
Error message
service %q pre_start[%d] exited with code %d
What it means
The pre_start hook container finished cleanly but with a non-zero status code. Compose reports service, hook index, and exit code, and gates the service from starting. This is the pre_start analogue of a failing init container in other orchestrators.
Source
Thrown at pkg/compose/pre_start.go:254
}
return preStartResultErr(serviceName, index, res)
case err := <-errCh:
if err != nil {
return err
}
// nil on Error: stream closed cleanly. Disable this case so a
// closed channel can't fire repeatedly.
errCh = nil
}
}
}
func preStartResultErr(serviceName string, index int, res container.WaitResponse) error {
if res.Error != nil {
return fmt.Errorf("service %q pre_start[%d] wait error: %s", serviceName, index, res.Error.Message)
}
if res.StatusCode != 0 {
return fmt.Errorf("service %q pre_start[%d] exited with code %d", serviceName, index, res.StatusCode)
}
return nil
}
// streamPreStartLogs returns a channel that is closed once the hook log stream
// has been fully drained (or never opened). Callers must wait on it before
// returning so the goroutine cannot outlive the hook.
func (s *composeService) streamPreStartLogs(ctx context.Context, containerID string, service types.ServiceConfig, index int, listener api.ContainerEventListener) <-chan struct{} {
done := make(chan struct{})
if listener == nil {
close(done)
return done
}
source := fmt.Sprintf("%s pre_start[%d] ->", service.Name, index)
logs, err := s.apiClient().ContainerLogs(ctx, containerID, client.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Follow: true,View on GitHub (pinned to ddc4b044b6)
Solutions
- Reproduce the hook manually in a container with the same image/volumes and inspect its output
- Fix the hook command/entrypoint (absolute paths, executable bit, correct working dir)
- Make migrations idempotent so re-runs succeed after partial failure
- Ensure the hook image actually contains the binaries the command references
Defensive patterns
Strategy: try-catch
Try / catch
if err := upWithPreStart(ctx, project); err != nil {
if strings.Contains(err.Error(), "pre_start") && strings.Contains(err.Error(), "exited with code") {
// re-run the hook manually in the same image/volumes to debug
}
} Prevention
- Make pre_start commands idempotent (migrations especially) so re-runs succeed
- Verify the hook image contains its dependencies before promoting to compose files
When it happens
Trigger: runPreStartHook's wait returns StatusCode != 0 with no daemon error: the hook command itself failed (missing binary, bad script, failed migration, etc.).
Common situations: Migration script failing on schema conflict; hook image missing the tool it invokes; wrong working_dir/command in the pre_start entry; script exiting on first error via set -e.
Related errors
- %s hook exited with status %d
- service %q pre_start[%d]: per_replica is not yet supported;
- service %q pre_start[%d] wait error: %s
- exit code %d
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/ee95df68974381e3.
Report an issue: GitHub.