e2b-dev / e2b-dev/runtime

[Bug]: flock() on a mounted volume hangs forever (mount is missing `nolock`)

Open Beginner friendly
#3,619 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
1.6k
Forks
438
PR merge metrics
No merged PRs in 30d

Description

Sandbox ID or Build ID

iu5wt6ki121fvvkv6cklc (stuck process), iqop9y9ik26oaud4l4tky (remount test)

Environment

e2b JS SDK 2.46.0, sandbox guest Ubuntu 24.04, host macOS 15.6.
Volumes mounted through volumeMounts on Sandbox.create.

Timestamp of the issue

2026-09-04 13:47 UTC

Frequency

Happens every time

Expected behavior

flock() on a file in a mounted volume either succeeds as a node-local lock, or fails promptly with an error such as ENOLCK.

Actual behavior

It blocks forever in uninterruptible sleep (state D). The process cannot be killed, not even with SIGKILL, and it holds the sandbox until the sandbox itself is destroyed.

Reads and writes on the volume are fine. Only locking hangs.

Kernel stack of the stuck process:

__do_sys_flock → nfs_flock → nfs3_proc_lock → nlmclnt_lock → nlmclnt_call → rpc_wait_bit_killable
Issue reproduction
  1. Create a volume and mount it on a sandbox:
const volume = await Volume.create('flock-repro')
const sbx = await Sandbox.create('<template>', {
  volumeMounts: { '/mnt/vol': volume },
})
  1. In the sandbox, take a lock on the volume and on local disk for comparison:
touch /mnt/vol/f /tmp/f
timeout 10 flock /mnt/vol/f -c true; echo $?   # 124, blocked
timeout 10 flock /tmp/f      -c true; echo $?  # 0
  1. The first command never returns. cat /proc/<pid>/stack shows the trace above, and cat /proc/<pid>/wchan reads rpc_wait_bit_killable.
Additional context

Cause. nfsOptions in packages/envd/internal/api/init.go:607 sets neither nolock
nor local_lock, so local_lock defaults to none and the kernel sends every lock to
the proxy as an NLM request. go-nfs has no lock manager, and the portmapper answers the
lookup for one with port 0 (pkg/portmap/main.go:59). Because the mount is hard, the
client retries the bind forever instead of failing.

Fix. Add "nolock" to nfsOptions. Locks then resolve locally, which is the correct
semantics anyway given the proxy has no lock manager.

Why this looks like an omission. The same server is mounted with nolock everywhere
else in this repo:

  • packages/orchestrator/pkg/nfsproxy/e2e_start.sh:29 mounts the nfsproxy with nolock,
    so the e2e suite never runs the configuration production uses.
  • The Filestore chunk cache mount sets "nolock", // do not use locking.
  • The host Filestore mount sets "lock", "local_lock=none" deliberately, where a real
    NLM-capable server is on the other end.

Only the sandbox volume mount leaves the choice unmade. Upstream has the same report
against go-nfs, closed by the reporter with "Using nolock solved this":
https://github.com/willscott/go-nfs/issues/98

Use case. We hit it with the Codex CLI, which takes an flock in $CODEX_HOME. The failure is silent: no error, no timeout, no log.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/envd/internal/api/init.go:607 and inspect how nfsOptions is used for sandbox volume mounts. Compare it with packages/orchestrator/pkg/nfsproxy/e2e_start.sh:29 and the Filestore mount configurations, then run the flock reproduction to confirm the mounted-volume lock returns promptly rather than hanging.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
infrastructure
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.