We run a userspace FUSE daemon inside a Docker container (LayerFS). On OrbStack, a plain umount2 detaches the mount, but the daemon's two read(/dev/fuse) workers stay blocked and the connection outlives the detach. A fusectl abort wakes them; creating the mount in a private mount namespace avoids the stall.
We are not claiming a confirmed OrbStack bug and we do not have a patch. We are asking whether this is expected, whether the kernel can expose a reliable "no mounts remain" signal, and whether the OrbStack kernel carries a change in this area.
Environment
- macOS 26.5.1 (Darwin 25.5.0, arm64)
- OrbStack 7.2.9
- Docker Engine 29.8.2 (API 1.54)
- Linux kernel
7.2.9-orbstack-00391-g2b298d05fbd4
The daemon is a normal userspace FUSE server; the container has CAP_SYS_ADMIN and /dev/fuse. The daemon is non-dumpable (PR_SET_DUMPABLE=0) and the container has no CAP_SYS_PTRACE, which limits what we can inspect from inside, not the kernel behavior.
Reproducer
Using the public LayerFS SDK against a disposable project:
sandbox.create() (start the container).
- Immediately
workspace.mount(sandbox, branch).
- Sleep 1500 ms.
- Plain
workspace.unmount(ws); record wall time and result.
- Clean up with a forced unmount, then close the Sandbox.
The exact Node probe is in the linked LayerFS #460 comment. It is deterministic for this shape of run: 6/6 at LayerFS 582bc786, 1/1 at v1.1.0. At 250 ms into a stalled unmount the mount row is already gone from the container's namespace, yet the readers are still blocked; the unmount returns only at its ~5 s Join deadline.
Timing
Delay before mount / delay before unmount, two runs each, unmount wall time:
| before mount |
before unmount |
unmount |
| 0 ms |
300 ms |
22 / 19 ms |
| 2500 ms |
1500 ms |
43 / 15 ms |
| 0 ms |
1500 ms |
5066 / 5029 ms |
| 2500 ms |
300 ms |
12 / 9 ms |
The mount must be created immediately after the container starts and live about 1.5 s for the stall to appear.
What we already know
- Readers stay blocked after detach.
umount2 returns success and the mount row is gone, but the two FUSE receive loops remain in read(/dev/fuse) and the connection is still alive.
fusectl abort works. Writing 1 to /sys/fs/fuse/connections/<n>/abort wakes the readers; drain + revoke + close then completes in p50 75 ms (max 558 ms). With a 1 s join grace the stall ends at about 1.08 s instead of about 5.1 s.
- A private mount namespace avoids it. If the daemon calls
unshare(CLONE_NEWNS) and makes / private before mounting, so the mount never appears in the container's initial namespace, early-mount unmount is p50 15 ms (n=10, range 7.2–35.0 ms). The same mount with the flag off stalls about 5.0 s.
- We could not find the holder. No process fd or cwd pointed into the mount; a privileged
--pid=host scan across namespaces found the FUSE mount only in the container's PID-1/daemon namespace, not in an additional holder. We cannot identify what keeps the superblock referenced.
For reference, a related layer-level report exists against Docker Desktop (LayerFS #426), so we are not assuming this is OrbStack-only.
Question
- Should a plain
umount2 detach wake or abort the blocked FUSE readers when the superblock is kept alive only by a mount in another namespace, or must the last reference drop first? Is the current behavior intended?
- Can the kernel expose a reliable "no active mounts/user" signal (for example a per-connection mount reference count or a "detached" state) so userspace can abort deterministically instead of guessing a grace period?
- Is there an OrbStack kernel patch in this area (FUSE abort/teardown or mount-namespace reference handling)? We could not read the
7.2.9 kernel source: the public orbstack/linux-macvirt tree we can inspect is older.
Links