Skip to content

Conversation

@MaheshThakur9152
Copy link
Contributor

Summary

  • When a network configuration (for example IPAM/subnet) changes, Compose stops containers and recreates the network.
  • The Docker engine can restart those containers automatically (e.g., due to a restart policy). Compose previously did not emit any event representing the containers' resumed state after reconnect, so the TTY UI could be left showing only "Stopped" even though the containers were actually running.
  • This PR makes Compose inspect containers after reconnecting them and emits the correct container status (Running / Created / Stopped), so the UI reflects the real state.

Root cause

  • The code path that handles diverged networks stops and disconnects containers, removes the network, recreates it and then reconnects containers.
  • If the engine restarts those containers between disconnection and reconnection, Compose had no further event for them, so the TTY progress UI only displayed the earlier Stopped.

What I changed

  • After reconnecting previously-dangled containers, inspect each container and emit one of the following events depending on the container state:
    • Running -> Running event
    • Created -> Created event
    • Exited -> Stopped event
  • Ignore inspect errors to avoid failing network creation due to races where a container disappears between disconnect and reconnect.
  • Add unit test TestResolveOrCreateNetworkEmitsRunningEvent to assert that a running event is emitted for a container restarted by the engine.

Files changed

  • create.go — emits container status events after reconnecting containers
  • create_test.go — new unit test simulating the diverged network flow and the engine restarting a container

Testing done

  • Unit test: go test ./pkg/compose -run TestResolveOrCreateNetworkEmitsRunningEvent -v — passes locally.
  • Full test suite: will run in CI (needs Docker daemon access and runs in their environments).

Notes for reviewers

  • Small, focused change to event emission — no new event types or UI changes.
  • Emitted events use existing statuses (Running, Created, Stopped) so the current UI picks them up.
  • If maintainers prefer a different message (e.g., “Reconnected”), I’m happy to adjust.
  • Signed-off-by: Mahesh Thakur maheshthakur9152@gmail.com

Fixes: #13524

Copilot AI review requested due to automatic review settings January 19, 2026 16:39
@MaheshThakur9152 MaheshThakur9152 requested a review from a team as a code owner January 19, 2026 16:39
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes issue #13524 where Docker Compose's TTY UI doesn't reflect the actual state of containers after they are automatically restarted by the Docker engine during a network reconfiguration. When a network's configuration diverges (e.g., IPAM/subnet changes), Compose stops and disconnects containers, recreates the network, and reconnects the containers. If the engine has a restart policy that automatically restarts those containers, the UI previously showed them as "Stopped" even though they were actually "Running".

Changes:

  • Adds logic to inspect and emit container status events after reconnecting containers to a recreated network
  • Adds a unit test to verify that a "Running" event is emitted for containers restarted by the engine during network recreation

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pkg/compose/create.go Added code to inspect containers after network reconnection and emit appropriate status events (Running, Created, or Stopped) based on their actual state
pkg/compose/create_test.go Added unit test TestResolveOrCreateNetworkEmitsRunningEvent to verify the new behavior and a test event processor helper

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1431 to 1437
if inspected.State != nil && inspected.State.Running {
s.events.On(runningEvent(name))
} else if inspected.State != nil && inspected.State.Status == container.StateCreated {
s.events.On(createdEvent(name))
} else if inspected.State != nil && inspected.State.Status == container.StateExited {
s.events.On(stoppedEvent(name))
}
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional logic is inconsistent: it checks inspected.State.Running (a boolean field) for running containers but checks inspected.State.Status (a string field) for created and exited containers. For consistency and correctness, all conditions should check the Status field. The first condition should be inspected.State.Status == container.StateRunning to match the pattern used in similar code elsewhere in the codebase (e.g., ps.go lines 72-77).

Copilot uses AI. Check for mistakes.
// ignore inspect errors
continue
}
name := "Container " + strings.TrimPrefix(inspected.Name, "/")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use getCanonicalContainerName for consistency with other places we produce events

}
}

func TestResolveOrCreateNetworkEmitsRunningEvent(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer use of e2e tests as mocks are a pain to maintain with many API calls, also in most case we don't get the actual engine behavior demonstrated by such tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi — thanks for the review and the suggestions!

I implemented both fixes you pointed out:

  • Switched to using the canonical container name (now using getContainerProgressName(tmp)) so emitted event names match other code paths.
  • Fixed the state check to consistently use the Status field (inspected.State.Status == container.StateRunning), consistent with other code.

Per your preference for e2e tests, I added an e2e test:

  • TestNetworkIPAMChangeReportsStarted changes the subnet and runs docker compose up --progress=plain, then asserts the output contains the expected container state transition (Stopped then Started or Recreated).

I left the focused unit test as an additional guard; if you prefer to rely only on e2e tests I can remove the unit test in a follow-up. Happy to tweak wording or add more coverage if you prefer.

— Mahesh

@MaheshThakur9152 MaheshThakur9152 force-pushed the fix/network-status-events-13524 branch 2 times, most recently from 24d36c1 to fb09730 Compare January 19, 2026 18:27
@ndeloof
Copy link
Contributor

ndeloof commented Jan 20, 2026

docker engine to automatically restart container is a corner case. The actual issue on #13524 is Compose not to log an event after a container is (re)started.

@MaheshThakur9152
Copy link
Contributor Author

docker engine to automatically restart container is a corner case. The actual issue on #13524 is Compose not to log an event after a container is (re)started.

Ack, understood. The auto-restart is just the trigger, but the missing event log is the gap in Compose.

This PR addresses that root cause: it inspects the container state after network reconciliation and emits the missing status event.

I've updated the logic and added the E2E test to cover this. Ready for review.

@ndeloof
Copy link
Contributor

ndeloof commented Jan 20, 2026

This PR addresses that root cause

it doesn't. It just emits an additional event, while this one should be managed by start

@MaheshThakur9152
Copy link
Contributor Author

This PR addresses that root cause

it doesn't. It just emits an additional event, while this one should be managed by start

Got it. So instead of manually emitting the event post-reconnect, we should ensure the standard start lifecycle handles this transition?

Could you point me to the specific start method/flow you are referring to? I want to make sure I hook into the correct lifecycle stage rather than patching it here.

@ndeloof
Copy link
Contributor

ndeloof commented Jan 20, 2026

this is intentional:

if w.operation != "start" && (e.Text == api.StatusStarted || e.Text == api.StatusStarting) {

if we don't, and compose run attached, the progress UI will collides with first logs
A fix could be to let the Full (tty.go) display be aware we run detached, and not hide events.

@MaheshThakur9152 MaheshThakur9152 force-pushed the fix/network-status-events-13524 branch 2 times, most recently from 3f53733 to 6de1e1e Compare January 20, 2026 13:07
@MaheshThakur9152
Copy link
Contributor Author

this is intentional:

if w.operation != "start" && (e.Text == api.StatusStarted || e.Text == api.StatusStarting) {

if we don't, and compose run attached, the progress UI will collides with first logs A fix could be to let the Full (tty.go) display be aware we run detached, and not hide events.

Updated! I've reverted the changes in create.go and moved the logic to tty.go as suggested. The display now allows StatusStarted events specifically when running in detached mode.
Validated with the E2E test. Ready for re-review.

@MaheshThakur9152 MaheshThakur9152 force-pushed the fix/network-status-events-13524 branch 2 times, most recently from 0f35cab to 58afa50 Compare January 20, 2026 16:28
ndeloof
ndeloof previously approved these changes Jan 21, 2026
display.Mode = display.ModeTTY
}

detached, _ := cmd.Flags().GetBool("detach")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this works, as detach flag is declared on child command

@ndeloof ndeloof enabled auto-merge (rebase) January 21, 2026 07:36
auto-merge was automatically disabled January 21, 2026 08:15

Head branch was pushed to by a user without write access

@MaheshThakur9152 MaheshThakur9152 force-pushed the fix/network-status-events-13524 branch 2 times, most recently from bb62ddd to daa6f4b Compare January 21, 2026 08:17
Makefile Outdated
BINARY_EXT=.exe
endif

ifeq ($(DETECTED_OS),Darwin)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was intentionnaly removed from main branch, now managed within Dockerfile

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was intentionnaly removed from main branch, now managed within Dockerfile

My apologies, that was a remnant from my local dev environment on macOS. I've reverted the Makefile changes. Thanks for catching that

Signed-off-by: Mahesh Thakur <maheshthakur9152@gmail.com>
@MaheshThakur9152 MaheshThakur9152 force-pushed the fix/network-status-events-13524 branch from daa6f4b to f41af27 Compare January 21, 2026 08:25
@ndeloof ndeloof enabled auto-merge (rebase) January 21, 2026 08:28
@ndeloof ndeloof merged commit a061c17 into docker:main Jan 21, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] misleading status for non-host-mode containers

3 participants