apple / apple/container

Per-container operations hang indefinitely (create stuck at [6/6]; teardown never completes after workload exit); hung ops cascade and can drop a custom network's gateway until the hung client is killed (1.0.0)

Open
#1,884 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
49.9k
Forks
1.8k
Avg merge
1d 20h
Merged PRs (30d)
22

Description

## Environment

- `container` 1.0.0 (Homebrew `container` keg 1.0.0_1)
- macOS 26 (Tahoe), Darwin 25.5.0, Mac Studio M3 Ultra (28 cores, 28% avg load during incidents)
- Custom NAT network: `container network create --subnet 192.168.72.0/24 runners` (helper cmdline: `container-network-vmnet start --id runners --service-identifier com.apple.container.network.container-network-vmnet.runners --mode nat --subnet 192.168.72.0/24`)
- Workload: ephemeral Linux containers used as GitHub Actions JIT runners — up to 10 concurrent, mostly 1–5 min lifetimes, 100–300 launches/hour in bursts, launches deliberately spaced ≥10 s apart (pacing did not prevent anything below)

## Summary

Under sustained short-lived-container churn we observe three related behaviors, all consistent with per-container XPC operations having **no timeout** and one hung operation blocking others behind it (head-of-line). Evidence timestamps are from 2026-07-02 (UTC).

### A. `container run` hangs at `[6/6] Starting container` indefinitely; the record is never created

- CLI spinner observed at `[6/6] Starting container [28m 52s]` … `[49m 38s]`.
- In one 95-minute window (17:31–19:05Z): **283 `container run` invocations → only 70 containers ever started their workload**; at peak **63 concurrent hung `container run` CLI processes**, while other containers on the same network booted normally seconds apart from the hung ones.
- A hung create never materializes a record (`container ls --all` does not show the name), so `container stop` / `container delete` are no-ops for it and the CLI waits forever. Killing the `container run` process is the only way to release it.

### B. Containers whose workload exited never tear down

- The containerized process (GitHub runner agent) completes and exits cleanly ~45 s after boot; `container ls` still shows `running` 65+ minutes later, and the per-container `container-runtime-linux` process persists.
- `container stop` on such a container sometimes works (returns in ~5 s and the blocked `container run` client then exits); at other times — concurrent with shape A — `container stop` itself hangs.

### C. While hung per-container processes exist, the custom network's gateway IP can vanish from the host; killing the hung processes restores it within ~1 second

Timeline:

- 01:03:30–31Z — three containers launched; their jobs completed 42–46 s later; the containers never tore down (shape B).
- 01:05:04Z — gateway `192.168.72.1` is no longer present on any host interface (`ifconfig`); guests lose TCP egress (in-container `nc -z 1.1.1.1 443` fails; workloads report lost network).
- 02:08:30Z and 02:09:37Z — the two hung containers are force-stopped (`container stop` worked here, ~5 s each).
- 02:09:44Z — **one second after the second cleanup — the gateway address is back** and subsequent launches get IPs + egress normally. Neither `container-apiserver` nor `container-network-vmnet` was restarted at any point; the network object was never deleted or recreated.
- The same drop→kill→instant-restore pattern reproduced three more times the same day. In one instance, killing **one** hung client caused **five** queued `container run` creates (hung 60+ min) to all return within one second — classic head-of-line release.
- The daemons never crash: the unified log shows only healthy XPC connection churn for `container-apiserver` in these windows, and `container-network-vmnet` logs **nothing at default level** across a 65-minute outage.

### Frequency

Some episode of A/B/C occurs every ~10–40 minutes under our churn. It reproduces with launches spaced 10–22 s apart, so a launch burst is not required.

## Ruled out

- **IP exhaustion** — /24 subnet, ≤ ~15 addresses in use; 1.0.0's connection-as-lease allocation (#1545) verifiably frees IPs when the hung processes are killed.
- **Host pressure** — 27–68% CPU, no memory pressure; a parallel Virtualization.framework workload (Tart macOS VMs) on the same host is unaffected throughout.
- **Guest images** — the same images boot in seconds once the hung client processes are killed.

## Related issues

#857 / #1162 (XPC timeout at containerCreate/ping — matches shape A's front edge), #861 (`system stop` hangs on the vmnet helper), #1321 (`vmnet_network_ref` lifecycle), #679 (force-quit via per-container `launchctl kill` — the workaround class we converged on), #1389 (TCP silently dropped with gateway present). What this report adds: the completed-workload-never-exits shape, the gateway-vanishes-while-hung-processes-exist ↔ restored-in-~1s-on-kill correlation, head-of-line release of queued creates, and rate/scale data from a CI fleet.

## Workaround that works reliably (no daemon restarts, no reboot)

Reconcile container state against external job state, then kill only per-container processes: TERM/KILL the hung `container run` client; if the record still shows `running`, TERM/KILL its `container-runtime-linux … --uuid ` process. This releases queued operations immediately, restores the gateway when it had dropped, and frees the IP. `container-apiserver` / `container-network-vmnet` never need to be touched.

## Asks

1. Timeouts on per-container XPC operations — the create/start path behind `[6/6]`, and the exit/teardown notification path — so a hung operation fails instead of blocking forever (related: your own CI timeout was raised to 75 min in #1663).
2. Per-operation logging (or queue visibility) in `container-network-vmnet` — it logs nothing at default level even during an hour-long outage, so the blocking operation is unidentifiable from outside.
3. Clarification whether one hung attach/detach blocking the helper (and dropping the gateway address from the host) is a known failure mode.

Happy to provide extended unified-log captures, run instrumented builds, or gather a sysdiagnose during the next episode — it recurs frequently enough that turnaround would be fast.

Contributor guide

Open the contributing guide

Research direction

Start with the per-container XPC create/start and exit/teardown paths in container-apiserver, then inspect container-network-vmnet's attach/detach handling. Reproduce the described short-lived-container churn while collecting the mentioned unified logs; done should mean bounded operations, no queued head-of-line hangs, and logging that identifies a blocker.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, swift
Domain
infrastructure, networking, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.