docker/compose · error

cannot take exclusive lock for project %q: %w

Error message

cannot take exclusive lock for project %q: %w

What it means

Returned by `docker compose watch` when locker.NewPidfile(project.Name) fails while creating the project lockfile (typically under ~/.docker/compose/ or the configured lock dir). NewPidfile errors when the pidfile path cannot be created or written (permissions, invalid project name characters, read-only home). The lock enforces a single active watch/up session per project, so creation failure aborts before any watch work.

Source

Thrown at cmd/compose/watch.go:99

	// so env_file declared by unrelated services doesn't need to exist
	project, err = project.WithServicesEnvironmentResolved(true)
	if err != nil {
		return err
	}

	if err := applyPlatforms(project, true); err != nil {
		return err
	}

	build, err := buildOpts.toAPIBuildOptions(nil)
	if err != nil {
		return err
	}

	// validation done -- ensure we have the lockfile for this project before doing work
	l, err := locker.NewPidfile(project.Name)
	if err != nil {
		return fmt.Errorf("cannot take exclusive lock for project %q: %w", project.Name, err)
	}
	if err := l.Lock(); err != nil {
		return fmt.Errorf("cannot take exclusive lock for project %q: %w", project.Name, err)
	}

	if !watchOpts.noUp {
		for index, service := range project.Services {
			if service.Build != nil && service.Develop != nil {
				service.PullPolicy = types.PullPolicyBuild
			}
			project.Services[index] = service
		}
		upOpts := api.UpOptions{
			Create: api.CreateOptions{
				Build:                &build,
				Services:             services,
				RemoveOrphans:        false,
				Recreate:             api.RecreateDiverged,

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Inspect the wrapped error's cause — it names the underlying file operation that failed.
  2. Fix permissions on the lock directory (commonly ~/.docker/compose or $XDG-style lockdir): `chmod`/`chown` it to the running user.
  3. If HOME is read-only (containers/CI), mount or point the lock location at a writable path, or run as a user with a writable home.
  4. As a last resort remove a stale/corrupt pidfile for the project after verifying no watch process is running.

Example fix

# before (lockdir not writable by current user)
docker compose watch

# after
chmod -R u+w ~/.docker/compose   # or run as the owning user
docker compose watch
Defensive patterns

Strategy: try-catch

Validate before calling

# ensure the lock dir is writable before watch
LOCKDIR="$HOME/.docker/compose"
[ -w "$LOCKDIR" ] || { echo "lock dir not writable: $LOCKDIR" >&2; exit 2; }

Try / catch

err := runWatch(...); if err != nil && strings.Contains(err.Error(), "cannot take exclusive lock") { /* inspect wrapped cause: perms/path; fix lockdir ownership or redirect to writable HOME; do NOT blindly retry */ }

Prevention

When it happens

Trigger: Unwritable lock directory (permissions changed, read-only filesystem, sandboxed container with read-only HOME); a project name that sanitizes to a path with invalid characters; disk-full or SELinux denial on the lockdir; running watch as a different user than the one owning existing lock files.

Common situations: Running `docker compose watch` inside a dev container or CI where HOME is read-only; root vs non-root ownership conflicts on the lock directory; systems with restrictive umask/ACLs on ~/.docker.

Related errors


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