nic@pype ~/increase-inotify-limit-in-your-ci-workers
infra · automation · writing
role: Disciple · Husband · Father · Developer
Increase inotify limit in your CI workers cover image

Increase inotify limit in your CI workers

Today I tripped over a CI failure that I had to think about for a while.

I build zensical static sites in CI on my Forgejo instance. These builds had been working fine, then suddenly started failing with no code changes. Naturally, I assumed something upstream broke — maybe a new uv release, maybe zensical.

  • I pinned versions.
  • I tested older versions.
  • Same failure every time.

That ruled out regressions and pushed me toward the environment.

I pulled the runner and worker images locally and built the sites just fine... But that doesn't perfectly emulate the CI setup - my forgejo runner relies on docker-in-docker and so we aren't just running a container on a host, we have this middle layer to consider... I wasn't sure how to really test this out locally so I succomed to AI and here's where Jipity got me in about 5 minutes...

The failure

The builds blew up with:


thread 'zrx/monitor' panicked at .../zensical-watch/src/agent/monitor.rs:154:49:
called `Result::unwrap()` on an `Err` value:
Error { kind: Io(Os { code: 24, message: "Too many open files" }) }

At first glance it means nothing to me but Jipity says this screams ulimit.

So following the AI overlords I checked:

ulimit -n was already very high

/proc/sys/fs/inotify/max_user_watches was also very high

PID limits were not constrained

Everything looked fine according to Jipity.

Yet the panic persisted.

The real culprit

The actual limit being hit was:

/proc/sys/fs/inotify/max_user_instances

In my Forgejo runner container, it was set to 128.

That turns out to be far too low for zensical.

Here's what ChatGPT said:

Even during a normal zensical build, the tool spins up its watch subsystem, which creates many inotify instances. Once it crosses the kernel limit, inotify_init() fails with EMFILE, and the process panics because the error is unwrapped.

The fix

Raising the inotify instance limit fixed it immediately:


echo 1024 > /proc/sys/fs/inotify/max_user_instances
RUST_BACKTRACE=full uvx zensical build --clean

After that, the build succeeded consistently.