Tech Digest

Guide

What should fs.inotify.max_user_watches be for Syncthing, Jellyfin or code-server in Docker?

Read the current value before you copy anyone's number: on a 5.11 or newer kernel with 32 GB of RAM it is already above the 204800 that Syncthing's FAQ recommends. We measured how the budget is shared between the host and containers, and made Syncthing, Jellyfin and code-server run out of it to record what each one prints.

Last reviewed

What should fs.inotify.max_user_watches be for Docker apps?

At least the number of directories every app running as that UID watches, with headroom; 524288 covers almost any home server. Check the current value first with cat /proc/sys/fs/inotify/max_user_watches: since Linux 5.11 the default is about 1% of RAM divided by the per-watch cost, clamped to 8,192 to 1,048,576, so a 32 GB host already has about 268,000. Set it on the host in /etc/sysctl.d/. A container cannot change it, and its UID 1000 shares the host's UID 1000 budget.

Enough for every directory that all apps running as the same UID watch, plus headroom, and on many hosts that is already the default. Since Linux 5.11 the kernel sets max_user_watches at boot to about 1% of RAM divided by the estimated cost of one watch, clamped between 8,192 and 1,048,576. The "default is 8192" advice in most forum threads describes kernels older than 5.11 or machines with under 1 GB of RAM. Check before you change anything:

bash
cat /proc/sys/fs/inotify/max_user_watches /proc/sys/fs/inotify/max_user_instances

If the first number is 8192 and you run a sync tool, a media server or an editor over a large tree, set it to 524288 on the host. If it is already in the hundreds of thousands, copying an old forum value can lower it.

Two answers circulate, and both are dated#

The first is "the default is 8192, raise it". That was true for every kernel from 2005 until the commit that changed it, whose message says the value "was set to 8192 since the introduction of the inotify feature in 2005" and is "just too small for many modern usage". That commit first shipped in 5.11, so any kernel from 5.11 on, on a machine with 8 GB, already starts at about 67,000.

The second is "use the number your app's docs give". Syncthing's FAQ gives 204800 and still says Linux "typically restricts the number of watches per user (usually 8192)". VS Code's documentation gives 524288. Both are fixed numbers written for the old default. On a modern kernel, Syncthing's 204800 is a reduction on any host with more than about 21 to 24 GB of RAM, and VS Code's 524288 is a reduction above about 53 to 63 GB.

What the default is on your host#

The formula in inotify_user.c is (RAM / 100) / INOTIFY_WATCH_COST, where the cost is the size of one inotify_inode_mark plus twice the size of a kernel struct inode, then clamped to [8192, 1048576]. The cost depends on the kernel build. On our 5.4 kernel, pahole read 80 bytes for the mark and 600 for the inode, so 1,280 bytes. VS Code's documentation uses 1,080. That gives a range:

RAM (MemTotal)Computed default, 5.11 and later
under about 0.85 to 1 GB8,192 (the floor)
2 GB16,774 to 19,880
4 GB33,552 to 39,765
8 GB67,107 to 79,534
16 GB134,217 to 159,072
32 GB268,435 to 318,145
64 GB536,870 to 636,290
about 105 to 125 GB and up1,048,576 (the ceiling)

These are computed from the formula, not read from machines of each size. MemTotal in /proc/meminfo is a little below the RAM you bought, so a 1 GB Raspberry Pi lands on the floor. The value our 24 GB host actually showed was 8192, because it runs kernel 5.4, which predates the change. On 5.11 or newer the same machine would compute about 196,000. Other defaults did not change: 128 instances per user and 16,384 queued events per instance, both set in the same function.

Distributions and packages can override the default with a file in /etc/sysctl.d/ or /usr/lib/sysctl.d/, so the cat above is the only number that counts.

The budget belongs to the host UID, and containers share it#

The inotify(7) man page says the limit is per "real user ID". The kernel source charges the count to current_euid() in the caller's user namespace, and the test below shows a user namespace does not escape the host's limit. Docker containers without user namespace remapping run in the host's namespace, so a container process running as UID 1000 spends the same budget as host UID 1000. We measured it on Engine 28.1.1 with max_user_watches at 8192:

TestWatches added before ENOSPC
Container as UID 1000, alone8,192
Host process as UID 1000 holding 6,000, then a container as UID 10002,192
A second container as UID 1000 from a different image, at the same time2,192
Container as UID 1001 at the same time8,192
Container as root7,079 (host root's daemons held the rest)
unshare -Ur as UID 1000, root inside, while UID 1000 held watches elsewherewhat was left of UID 1000's budget

The user namespace in the last row reported its own limit as 2,147,483,647 in /proc/sys/user/max_inotify_watches, and still stopped at the host UID's 8192. By the same mechanism, rootless containers and unprivileged LXC containers are bound by the budget of the host UID their user maps to.

This is why the limit runs out on boxes that look idle. Jellyfin, Syncthing and every LinuxServer.io image with PUID=1000 are typically the same UID as the first desktop user, so a media server, a sync tool, an editor and the host's own desktop session all draw from one pool. Why a container gets permission denied on a bind mount explains why so many stacks settle on 1000 in the first place. Giving each app its own UID gives each its own budget.

To see who holds the watches, as root on the host:

bash
for p in /proc/[0-9]*; do n=$(cat $p/fdinfo/* 2>/dev/null | grep -c '^inotify wd'); [ "$n" -gt 0 ] && echo "$n $(stat -c %U $p) $(cat $p/comm)"; done | sort -rn | head

Set it on the host, because the container cannot#

Every way of setting it from the container side failed, except the one that changes the host anyway:

  • sysctl -w fs.inotify.max_user_watches=524288 inside a container: Read-only file system.
  • docker run --sysctl fs.inotify.max_user_watches=524288: refused, sysctl ... is not allowed. Docker's reference lists only IPC and net.* sysctls as namespaced and says it "does not support changing sysctls inside of a container that also modify the host system".
  • Compose sysctls: with the same key: the container fails to start with sysctl "fs.inotify.max_user_watches" is not in a separate kernel namespace.
  • --privileged and sysctl -w: it worked, and /proc/sys/fs/inotify/max_user_watches on the host changed with it.

So put it on the host, in its own file, which survives reboots and package upgrades:

bash
printf 'fs.inotify.max_user_watches = 524288\nfs.inotify.max_user_instances = 512\n' | sudo tee /etc/sysctl.d/90-inotify.conf
sudo sysctl --system

The kernel checks the limit each time a watch is added, so running containers see the new value at once, though an app that gave up, like Jellyfin below, needs a restart. VS Code's documentation points Arch and Ubuntu 24.10 and later users to /etc/sysctl.d/ rather than /etc/sysctl.conf. On Proxmox, set it on the Proxmox host, not inside the LXC; the reasons the kernel belongs to the host are covered in Docker in LXC or a VM on Proxmox.

How big? inotify(7) says monitoring "is not recursive", so a recursive watcher spends one watch per directory, not per file. Count the directories under everything the UID watches, find /path/to/media /path/to/sync -type d | wc -l, and set at least double that. Raising the ceiling costs nothing until watches are used: about 1,080 bytes each by VS Code's estimate, about 540 MiB if all 524,288 are taken.

The error each app prints#

We starved UID 1000 of watches from the host, leaving 92, and pointed each app at a tree of 301 directories.

App, versionWhat it loggedRecovered when watches were freed?
Syncthing v2.1.7WRN Failed to start filesystem watcher (wait=1m0s error="failed to set up inotify handler. Please increase inotify limits, see https://docs.syncthing.net/users/faq.html#inotify-limits" ...)Yes, on its own, with no log line
Jellyfin 12.3.0 (jellyfin/jellyfin)[ERR] ... LibraryMonitor: Error in Directory watcher for: /media then System.IO.IOException: The configured user limit (8192) on the number of inotify watches has been reached, or the operating system failed to allocate a required resource.No, until the container restarted
code-server 4.143.1 (Code 1.143.1)Browser: Unable to watch for file changes. Please follow the instructions link to resolve this issue. Server log remoteagent.log: [File Watcher ('parcel')] Inotify limit reached (ENOSPC)Not tested
Your own processinotify_add_watch: No space left on device

Syncthing is the most forgiving. Its log format has changed since the FAQ quotes it, so search for Failed to start filesystem watcher. After we released the watches, the folder held 301 watches within three minutes, and the recovery was not logged at the default level, so check the folder in the web UI rather than waiting for a message.

Jellyfin is the trap. Its last log line was Watching directory /media, after the error, which reads as success. The process held 92 watches for a 301-directory library, one per directory at most, so changes in the other 209 directories were invisible to it. After a restart it held 304. Raise the limit, then restart Jellyfin. The number in its message is read from /proc/sys/fs/inotify/max_user_watches, so it shows the host's value. The .NET runtime has a matching message for instances: The configured user limit (128) on the number of inotify instances has been reached. Current .NET shares one inotify instance across all watchers in a process, so if you see that one, look first at other processes running as the same UID, or at the per-process file limit the message also names.

code-server shows its warning once, as a sticky notification whose never-show-again option is scoped to the workspace, so once dismissed it stays gone for that folder while the watcher stays broken. VS Code's own advice is to exclude heavy folders such as .venv in files.watcherExclude before raising the limit; node_modules and .git subfolders are excluded by default. The code-server profile lists the other traps.

The raw kernel error misleads people into checking df. ENOSPC from inotify_add_watch means the watch count, and EMFILE from inotify_init means the instance count: glibc prints Too many open files, musl No file descriptors available. We opened 128 instances as UID 1000 and the 129th failed.

When this answer stops applying#

  • Network shares. inotify(7) says it "does not catch remote events that occur on network filesystems". A library on NFS or SMB needs the app's polling or scheduled scan, whatever the limit; Paperless-ngx vs Nextcloud shows the same failure for a consume folder, and Why SQLite says database is locked on an NFS or SMB share covers the other problem with app data on a share.
  • Kernels without the February 2026 fix. Commit 6a320935fa42 fixed a count leak where failed watch additions could exhaust max_user_watches "even when no watches are active". It is marked for stable kernels. If the per-process count above is far below the limit and ENOSPC continues, a reboot clears it.
  • Docker Desktop. We tested Linux Engine only. Docker Desktop runs containers on its own VM's kernel, so the limit that counts is that VM's, not your workstation's.
  • Other watchers. We did not test Plex, Nextcloud, Immich or the Servarr apps. The per-UID accounting is the kernel's and applies to all of them; only the message differs.

What to do next#

Run the cat at the top. If it says 8192, write the two-line file into /etc/sysctl.d/, run sysctl --system, and restart Jellyfin, which does not retry. If it is already large, leave it alone unless the find count says otherwise, and give heavy watchers their own UID instead of piling everything onto 1000. While you are on the host, compare its RAM with the table above; Choosing home server hardware covers how much a home server actually needs. Keep the sysctl file with the rest of your host setup, alongside the stack layout in Docker Compose conventions, so the next machine gets it too.

Questions#

Is the default max_user_watches still 8192?

Only on kernels older than 5.11, or on machines with less than about 1 GB of RAM. Linux 5.11 changed the default to 1% of RAM divided by the estimated cost of one watch, kept between 8,192 and 1,048,576. On our 5.4 kernel with 24 GB the value was 8192. The same machine on a 5.11 or newer kernel would get about 196,000. Run cat /proc/sys/fs/inotify/max_user_watches rather than trusting either number.

Can I set fs.inotify.max_user_watches inside a Docker container?

No. /proc/sys is read only in a normal container, so sysctl -w fails with Read-only file system. docker run --sysctl refuses it as not allowed, and a Compose sysctls: entry fails at start with sysctl "fs.inotify.max_user_watches" is not in a separate kernel namespace. A --privileged container can write it, and in our test that changed the host's value for every user, which is the same as setting it on the host.

Does raising max_user_watches use more memory?

Not by itself. The value is a ceiling, and the kernel only counts a watch when a program adds one. Memory is spent per watch actually in use. VS Code's documentation puts each at 1,080 bytes and the full 524,288 at about 540 MiB. That is the worst case for one UID that uses its whole budget, not a reservation.

What does inotify_add_watch ENOSPC mean when the disk is not full?

It means the user ran out of inotify watches. The kernel returns ENOSPC from inotify_add_watch when the per-user watch count would exceed max_user_watches, and the C library turns that into the text No space left on device. Programs that print the raw error mislead people into checking df. Raise fs.inotify.max_user_watches on the host.

What is max_user_instances and do I need to raise it?

It caps how many inotify instances, not watches, one user can open, and the kernel default is 128. Each watching program opens one or more. When it runs out, inotify_init fails with EMFILE, which glibc prints as Too many open files and musl as No file descriptors available. On a host where many containers run as the same UID, raise it on the host too, for example to 512.

Sources#

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.