docker/compose · error

hit OS limits creating a watcher. Run 'sysctl fs.inotify.max

Error message

hit OS limits creating a watcher.
Run 'sysctl fs.inotify.max_user_instances' to check your inotify limits.
To raise them, run 'sudo sysctl fs.inotify.max_user_instances=1024'

What it means

This is the very first operation of newWatcher on the naive path: creating the fsnotify watcher itself allocates a new inotify instance on Linux. When the kernel refuses with 'too many open files' (EMFILE — max_user_instances reached), the code detects the string and returns a actionable remediation message instead of a cryptic error. It includes the exact sysctl commands to inspect and raise the limit.

Source

Thrown at pkg/watch/watcher_naive.go:304

	}
	return true
}

func (d *naiveNotify) add(path string) error {
	err := d.watcher.Add(path)
	if err != nil {
		return err
	}
	d.numWatches++
	numberOfWatches.Add(1)
	return nil
}

func newWatcher(paths []string) (Notify, error) {
	fsw, err := fsnotify.NewWatcher()
	if err != nil {
		if strings.Contains(err.Error(), "too many open files") && runtime.GOOS == "linux" {
			return nil, fmt.Errorf("hit OS limits creating a watcher.\n" +
				"Run 'sysctl fs.inotify.max_user_instances' to check your inotify limits.\n" +
				"To raise them, run 'sudo sysctl fs.inotify.max_user_instances=1024'")
		}
		return nil, fmt.Errorf("creating file watcher: %w", err)
	}
	MaybeIncreaseBufferSize(fsw)

	err = fsw.SetRecursive()
	isWatcherRecursive := err == nil

	wrappedEvents := make(chan FileEvent)
	notifyList := make(map[string]bool, len(paths))
	if isWatcherRecursive {
		paths = pathutil.EncompassingPaths(paths)
	}
	for _, path := range paths {
		path, err := filepath.Abs(path)
		if err != nil {

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Follow the message: run `sudo sysctl fs.inotify.max_user_instances=1024` (inspect first with `sysctl fs.inotify.max_user_instances`).
  2. Persist across reboots: `echo fs.inotify.max_user_instances=1024 | sudo tee /etc/sysctl.d/90-inotify.conf && sudo sysctl --system`.
  3. Reduce competing watchers: close unused editor windows/sync clients or stop other compose watch sessions to free instances.
  4. For Docker Desktop, raise the limit inside the VM's settings or increase allocated resources.

Example fix

# before: default
sysctl fs.inotify.max_user_instances   # 128 -> watchers fail
# after
sudo sysctl fs.inotify.max_user_instances=1024
Defensive patterns

Strategy: validation

Validate before calling

// Linux: fail fast if inotify instances are already exhausted
func inotifyHeadroom() error {
    max, _ := strconv.Atoi(strings.TrimSpace(mustRead("/proc/sys/fs/inotify/max_user_instances")))
    open := countOpenInotifyFds() // scan /proc/self/fd for 'inotify' links
    if open >= max { return fmt.Errorf("inotify instances exhausted; raise fs.inotify.max_user_instances") }
    return nil
}

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "hit OS limits creating a watcher") {
        // terminal config error: print sysctl remediation, do not retry programmatically
    }
    return err
}

Prevention

When it happens

Trigger: fsnotify.NewWatcher() failing with EMFILE on Linux: each NewWatcher consumes one inotify instance, and the default max_user_instances (often 128) is exhausted by other watchers (editors, dev servers, sync clients, other compose sessions) before compose starts.

Common situations: Long-running developer workstations with many file-watching apps (VS Code, Dropbox, JetBrains IDEs); CI runners or containers with low instance caps; Docker Desktop's Linux VM hitting its limit; reopening many compose watch projects in parallel.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/99232becee4102b3. Report an issue: GitHub.