Skip to content

[BUG] Attached up never exits after external stop+down when a container is in restart backoff (regression in v2.39.3) #13985

Description

@otomon10

Description

Since Compose v2.39.3, an attached docker compose up never exits when the project is stopped externally (docker compose stop + docker compose down from another process) while one of the containers is in restart backoff (Status=restarting with restart: unless-stopped).

Up to v2.39.2 the attached up exited a few seconds after down completed. From v2.39.3 on it hangs forever, even though all containers are stopped and removed. The behavior is still present in v5.3.1 (latest at the time of writing).

The apparent mechanism, observed via docker events during the stop window:

  • a container that is stopped while it is in restart backoff emits only stopdestroy events — there is no die event, because no process is running at that moment;
  • containers that are running normally when stopped emit the full killstopdiedestroy sequence;
  • since v2.39.3 (detect container is restarted #13210), the attached up appears to wait for a die event per container to detect termination, so it waits forever for the container that never emits one.

This matters for supervisors that spawn an attached up as a child process and stop the project externally: the leftover up process keeps holding resources it inherited (in our case, flock file locks on the compose files taken by a wrapper), long after the project is gone.

Steps To Reproduce

docker-compose.yml:

services:
  crasher:
    image: busybox:latest
    command: ["false"]
    restart: unless-stopped
  sleeper:
    image: busybox:latest
    command: ["sleep", "3600"]
  1. docker compose -p repro up --remove-orphans --force-recreate & (attached, in background)
  2. Wait until the crasher is in restart backoff: docker inspect repro-crasher-1 --format '{{.State.Status}} {{.RestartCount}}' shows restarting with RestartCount >= 5
  3. From another shell: docker compose -p repro stop -t 5 && docker compose -p repro down -t 5
  4. Observe the backgrounded up process:
    • v2.39.2 and earlier: exits within a few seconds after down
    • v2.39.3 and later (incl. v5.0.2, v5.3.1): never exits (waited 120+ s)

Bisect over official release binaries (same engine, same procedure, PASS = up exits / FAIL = up hangs):

version result
v2.15.1 PASS
v2.26.0 PASS
v2.33.1 PASS
v2.37.3 PASS
v2.38.2 PASS
v2.39.0 PASS
v2.39.1 PASS
v2.39.2 PASS
v2.39.3 FAIL (first bad release)
v2.39.4 FAIL
v5.0.2 FAIL
v5.3.1 FAIL

We also verified on a separate machine (same engine v29.3.0) that swapping only the compose CLI plugin between v5.0.2 and v2.38.2 toggles the hang with an identical docker events sequence, so the regression is in compose, not in the engine.

Compose Version

Docker Compose version v5.0.2
(also reproduced with v2.39.3, v2.39.4, v5.3.1; not reproducible with v2.39.2 and earlier)

Docker Environment

Reproduced on:
- Docker Engine 29.6.2 (amd64, WSL2) — bisect environment
- Docker Engine 29.3.0 (amd64, embedded Linux device) — production environment
Client: docker compose CLI plugin, official release binaries

Anything else?

The first bad release v2.39.3 contains #13210 ("use events to detect container exit"), which matches the observed mechanism (no die event is emitted for a container stopped during restart backoff).

Possibly related: moby/moby#50908 (open) adds a restarting attribute to die events for #13210's event-based detection. However, in this scenario the container emits no die event at all (only stopdestroy), so the attribute alone would not cover this case.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions