Skip to content

pre_start hooks: per_replica: true is accepted by the schema but always rejected at runtime #14259

Description

@ndeloof

What this issue is about

pre_start hooks support a per_replica attribute in the compose-spec model (types.PreStartHook.PerReplica, compose-go's types/hooks.go): "runs the hook once per service replica instead of once per service." docker/compose's engine, however, does not implement it: runPreStart (pkg/compose/pre_start.go) validates every declared hook up front and rejects the whole service if any hook sets per_replica: true:

for i, hook := range service.PreStart {
    if hook.PerReplica {
        return fmt.Errorf("service %q pre_start[%d]: per_replica is not yet supported; remove per_replica or set it to false", service.Name, i)
    }
}

So today, per_replica: true is accepted by the loader/schema but always a hard error at runtime — there is no path to actually use it.

Illustration

services:
  app:
    image: alpine
    command: sleep infinity
    deploy:
      replicas: 3
    pre_start:
      - image: alpine
        command: ["sh", "-c", "echo initializing replica"]
        per_replica: true
$ docker compose up -d
service "app" pre_start[0]: per_replica is not yet supported; remove per_replica or set it to false

The only way to get this compose file to run at all today is to drop per_replica: true, at which point the hook runs once for the whole service (against the lowest-numbered replica only, per the current one-hook-one-runner model) instead of once per replica — a different, sometimes unacceptable behavior for hooks that need to prepare per-replica state (e.g. per-replica config files, per-replica volume initialization).

Suggested scope for an implementation

The reconciliation-plan work in #14221 (pre_start hook runners as first-class plan resources) planned one CreateHookContainer operation per declared hook, targeted at a single resolved replica. Supporting per_replica: true would mean planning one runner per hook × per replica instead, with:

  • a deterministic name/label scheme distinguishing replicas (e.g. extending the existing com.docker.compose.hook-index label with a replica index, or a dedicated label),
  • VolumesFrom resolved against that replica rather than the lowest-numbered one,
  • the corresponding execution-phase change in runPreStart/execPreStartHook to run and account for one runner per replica.

This issue only tracks the feature gap; the reconciliation-plan groundwork from #14221 already discussed and worked around it (see its planPreStartHookRunners, which explicitly skips planning entirely for a service with any per_replica: true hook, to avoid orphaning runners that could never execute).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions