Skip to content
This repository was archived by the owner on Aug 18, 2025. It is now read-only.

Commit 7288ab0

Browse files
author
Jonathan Yu
committed
chore: document inotify watch limit
Add a guide explaining the inotify watch limit problem, including instructions to diagnose and resolve it.
1 parent eb31789 commit 7288ab0

File tree

3 files changed

+272
-2
lines changed

3 files changed

+272
-2
lines changed

.markdownlint.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"first-line-heading": false,
33
"no-inline-html": false,
4-
"ol-prefix": false
4+
"ol-prefix": false,
5+
"MD013": {
6+
"code_blocks": false
7+
}
58
}
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
---
2+
title: Troubleshooting inotify Watcher Limit Problems
3+
description: Learn how to resolve problems related to the inotify Watcher Limit.
4+
---
5+
6+
The [`inotify` facility] allows programs to monitor files and directories for
7+
changes, so that they will receive an event immediately whenever a user or
8+
program modifies the file or directory. Many such programs can fall back to
9+
periodically checking files and directories for changes, also known as polling.
10+
Using polling avoids the watch limit, but may result in higher system loading
11+
and a longer interval for the program to detect changes.
12+
13+
`inotify` requires kernel resources (memory and processor) for each file it
14+
tracks. As a result, the Linux kernel limits the number of file watchers that
15+
each user can register. The default settings vary according to the host system
16+
distribution; on Ubuntu 20.04 LTS, the default limit is 8,192 watches per instance
17+
18+
[`inotify` facility]: https://en.wikipedia.org/wiki/Inotify
19+
20+
With some applications and tools, including Webpack or [code-server], you may
21+
encounter an error similar to the following:
22+
23+
> Watchpack Error (watcher): Error: ENOSPC: System limit for number of file
24+
> watchers reached, watch '/some/path'
25+
26+
[code-server]: https://github.com/cdr/code-server
27+
28+
On a 64-bit system, each `inotify` watch that programs register will consume
29+
approximately 1 kB of kernel memory, which cannot be swapped to disk and is not
30+
counted against the environment memory limit setting.
31+
32+
## Diagnosis
33+
34+
If the total number of watchers is close to the `max_user_watches` setting, then
35+
it is likely that you are encountering this limit.
36+
37+
### Check tunable settings
38+
39+
There are three kernel tuning options related to the `inotify` system:
40+
41+
- `fs.inotify.max_queued_events`, which defines an upper bound on the number of
42+
file notification events pending delivery to programs.
43+
- `fs.inotify.max_user_instances`, which determines the maximum number of
44+
`inotify` instances per user. Programs using `inotify` will typically create
45+
a single _instance_, so this limit is unlikely to cause issues.
46+
- `fs.inotify.max_user_watches`, which defines the maximum number of files and
47+
folders that programs can monitor for changes.
48+
49+
For additional detail regarding the `inotify` system, please see
50+
[inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
51+
52+
To see the values for these settings applicable to your environment, run the
53+
following commands:
54+
55+
```shell-session
56+
$ sysctl fs.inotify.{max_queued_events,max_user_instances,max_user_watches}
57+
fs.inotify.max_queued_events = 16384
58+
fs.inotify.max_user_instances = 128
59+
fs.inotify.max_user_watches = 8192
60+
```
61+
62+
Because these settings are not namespace-aware, the values will be the same
63+
regardless of whether you run the commands on the host system or inside a
64+
container running on that host.
65+
66+
### Identify inotify consumers
67+
68+
To identify the programs consuming `inotify` watches, you can use a script that
69+
summarizes the information available in the `/proc` filesystem, such as
70+
[`inotify-consumers`]. This script will show the names of programs along with
71+
the number of `inotify` watches registered with the kernel:
72+
73+
```shell-session
74+
$ ./inotify-consumers
75+
INOTIFY
76+
WATCHER
77+
COUNT PID USER COMMAND
78+
--------------------------------------
79+
269 254560 coder /opt/coder/code-server/lib/node /opt/coder/code-server/lib/vscode/out/bootstrap-fork --type=watcherService
80+
5 1722 coder /opt/coder/code-server/lib/node /opt/coder/code-server/lib/vscode/out/vs/server/fork
81+
2 254538 coder /opt/coder/code-server/lib/node /opt/coder/code-server/lib/vscode/out/bootstrap-fork --type=extensionHost
82+
2 1507 coder gpg-agent --homedir /home/coder/.gnupg --use-standard-socket --daemon
83+
84+
278 WATCHERS TOTAL COUNT
85+
```
86+
87+
Please note that this is a third-party script published by an individual who is
88+
not affiliated with Coder, and as such, we cannot provide a warranty or support
89+
for its usage.
90+
91+
[`inotify-consumers`]: https://github.com/fatso83/dotfiles/blob/master/utils/scripts/inotify-consumers
92+
93+
To see the specific files and directories that the tools track for changes, you
94+
can use `strace` to monitor invocations of the `inotify_add_watch` system call:
95+
96+
```shell-session
97+
$ strace --follow-forks --trace='inotify_add_watch' inotifywait --quiet test
98+
inotify_add_watch(3, "test", IN_ACCESS|IN_MODIFY|IN_ATTRIB|IN_CLOSE_WRITE|IN_CLOSE_NOWRITE|IN_OPEN|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF) = 1
99+
```
100+
101+
This shows that the `inotifywait` command is listening for notifications related
102+
to the `test` file.
103+
104+
## Resolution
105+
106+
If you encounter the file watcher limit, there are two potential resolutions:
107+
either reduce the number of file watcher registrations, or increase the maximum
108+
file watcher limit. We recommend attempting to reduce the file watcher
109+
registrations first, because increasing the number of file watches may result
110+
in high processor utilization.
111+
112+
### Reduce watchers
113+
114+
Many applications include files that change very rarely, and developers often
115+
do not consider these to be part of _their_ application. For example, this may
116+
include third-party dependencies stored in `node_modules`. Tools may watch for
117+
changes to these files and folders, consuming `inotify` watchers.
118+
119+
These tools typically provide configuration settings to exclude certain files,
120+
paths, and patterns from file watching. For example, Visual Studio Code and
121+
`code-server` apply the following [user workspace setting] by default:
122+
123+
```json
124+
"files.watcherExclude": {
125+
"**/.git/objects/**": true,
126+
"**/.git/subtree-cache/**": true,
127+
"**/node_modules/**": true,
128+
"**/.hg/store/**": true
129+
},
130+
```
131+
132+
Consider adding other infrequently-changed files to this list, which will cause
133+
Visual Studio Code to poll for changes to those files periodically. For other
134+
software tools, please see the respective user manual.
135+
136+
[user workspace setting]: https://code.visualstudio.com/docs/getstarted/settings
137+
138+
### Increase the watch limit
139+
140+
You can increase the kernel tunable option to increase the maximum number of
141+
`inotify` watches for each user. This is a global setting that applies to all
142+
users sharing the same system, or _Node_ in Kubernetes parlance. To do this,
143+
modify the `sysctl` configuration file, or apply a DaemonSet to the Kubernetes
144+
cluster to apply that change to all nodes automatically.
145+
146+
For example, create a file called `/etc/sysctl.d/watches.conf` and include the
147+
following contents:
148+
149+
```text
150+
fs.inotify.max_user_watches = 65536
151+
```
152+
153+
Alternatively, you can use the following DaemonSet with `kubectl apply`:
154+
155+
```yaml
156+
apiVersion: apps/v1
157+
kind: DaemonSet
158+
metadata:
159+
name: more-fs-watchers
160+
namespace: kube-system
161+
labels:
162+
app: more-fs-watchers
163+
k8s-app: more-fs-watchers
164+
spec:
165+
selector:
166+
matchLabels:
167+
k8s-app: more-fs-watchers
168+
template:
169+
metadata:
170+
labels:
171+
name: more-fs-watchers
172+
k8s-app: more-fs-watchers
173+
annotations:
174+
seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default
175+
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
176+
spec:
177+
nodeSelector:
178+
kubernetes.io/os: linux
179+
initContainers:
180+
- name: sysctl
181+
image: alpine:3
182+
command:
183+
- sysctl
184+
- -w
185+
- fs.inotify.max_user_watches=16384
186+
resources:
187+
requests:
188+
cpu: 10m
189+
memory: 1Mi
190+
limits:
191+
cpu: 100m
192+
memory: 5Mi
193+
securityContext:
194+
# We need to run as root in a privileged container to modify
195+
# /proc/sys on the host (for sysctl)
196+
runAsUser: 0
197+
privileged: true
198+
readOnlyRootFilesystem: true
199+
capabilities:
200+
drop:
201+
- ALL
202+
containers:
203+
- name: pause
204+
image: k8s.gcr.io/pause:3.5
205+
command:
206+
- /pause
207+
resources:
208+
requests:
209+
cpu: 10m
210+
memory: 1Mi
211+
limits:
212+
cpu: 100m
213+
memory: 5Mi
214+
securityContext:
215+
runAsNonRoot: true
216+
runAsUser: 65535
217+
allowPrivilegeEscalation: false
218+
privileged: false
219+
readOnlyRootFilesystem: true
220+
capabilities:
221+
drop:
222+
- ALL
223+
terminationGracePeriodSeconds: 5
224+
```
225+
226+
This DaemonSet will ensure that the corresponding Pod runs on _every_ Linux node
227+
in the cluster. When new nodes join the cluster, such as when an autoscaling
228+
event occurs, the DaemonsSet will ensure that the pod runs on that node as well.
229+
You may delete the DaemonSet by running the following command:
230+
231+
```shell-session
232+
$ kubectl delete --namespace=kube-system daemonset more-fs-watchers
233+
daemonset.apps "more-fs-watchers" deleted
234+
```
235+
236+
However, note that the setting will persist until the node restarts or until
237+
another program sets the `fs.inotify.max_user_watches` setting.
238+
239+
## See Also
240+
241+
- Resources for Visual Studio Code and code-server:
242+
- [User and Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings),
243+
in particular, the setting called `files.watcherExclude`.
244+
- [VS Code Setting: files.watcherExclude](https://youtu.be/WMNua0ob6Aw)
245+
(YouTube).
246+
- [My ultimate VSCode
247+
configuration](https://dev.to/vaidhyanathan93/ulitmate-vscode-configuration-4i2o),
248+
a blog post describing a user's preferred settings, including file exclusions.
249+
- [Filesystem notification, part 1: An overview of dnotify and inotify](https://lwn.net/Articles/604686/)
250+
and [Filesystem notification, part 2: A deeper investigation of inotify](https://lwn.net/Articles/605128/)
251+
examine the `inotify` mechanism in detail, along with its predecessor,
252+
`dnotify`.
253+
- Microsoft's Language Server Protocol (LSP) specification describes an approach
254+
for using file watch notifications:
255+
[DidChangeWatchedFiles Notification](https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeWatchedFiles).
256+
Visual Studio Code and code-server, along with many other editors, uses this
257+
protocol for programming language support, and the same constraints and
258+
limitations apply to those tools.
259+
- Kubernetes provides a mechanism for [Setting Sysctls for a Pod](https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/#setting-sysctls-for-a-pod).
260+
However, the `inotify` tunable options cannot be set this way, so this is not
261+
applicable to resolving this issue.
262+
- [INotify watch limit](https://blog.passcod.name/2017/jun/25/inotify-watch-limit.html)
263+
provides additional context on the problem and its resolution.
264+
- [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html), the Linux
265+
manual page related to the `inotify` system call.
266+
- [Kernel Korner - Intro to inotify](https://www.linuxjournal.com/article/8478)

manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@
117117
"children": [
118118
{ "path": "./guides/admin/resources.md" },
119119
{ "path": "./guides/admin/usage-monitoring.md" },
120-
{ "path": "./guides/admin/helm-charts.md" }
120+
{ "path": "./guides/admin/helm-charts.md" },
121+
{ "path": "./guides/admin/inotify-watch-limits.md" }
121122
]
122123
},
123124
{

0 commit comments

Comments
 (0)