docker/compose · error

process with PID %d is still running

Error message

process with PID %d is still running

What it means

pidfile.Write implements single-instance locking: it reads the existing pidfile, and Read returns a non-zero PID only when that PID is currently alive (see the alive(pid) check feeding this path). A live old PID means another instance holds the lock, so Write refuses to overwrite the file. This is intentional mutual exclusion, not corruption.

Source

Thrown at internal/pidfile/pidfile.go:64

	if pid != 0 && alive(pid) {
		return pid, nil
	}
	return 0, nil
}

// Write writes a "PID file" at the specified path. It returns an error if the
// file exists and contains a valid PID of a running process, or when failing
// to write the file.
func Write(path string, pid int) error {
	if pid < 1 {
		return fmt.Errorf("invalid PID (%d): only positive PIDs are allowed", pid)
	}
	oldPID, err := Read(path)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	if oldPID != 0 {
		return fmt.Errorf("process with PID %d is still running", oldPID)
	}
	return os.WriteFile(path, []byte(strconv.Itoa(pid)), 0o644)
}

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Stop the running instance first (kill <oldPID>, or the service's stop command), then start again.
  2. Confirm the PID is genuinely yours: ps -p <oldPID>; if it is an unrelated process (PID reuse), remove the stale pidfile manually and retry.
  3. Wire the pidfile path into your init system so restarts handle cleanup (systemd manages this without pidfiles).
  4. Have your supervisor wait for process exit (e.g. ExecStop/killwait) before restarting.

Example fix

# before
$ mydaemon --pidfile /run/app.pid   # second instance: process with PID 4242 is still running

# after
$ kill 4242 && mydaemon --pidfile /run/app.pid
# if 4242 is unrelated (PID reuse): rm /run/app.pid && mydaemon --pidfile /run/app.pid
Defensive patterns

Strategy: try-catch

Validate before calling

if oldPID, err := pidfile.Read(path); err == nil && oldPID != 0 {
    if syscall.Kill(oldPID, 0) == nil {
        return fmt.Errorf("another instance is running as PID %d; stop it first", oldPID)
    }
}

Type guard

func instanceHoldsLock(path string) (int, bool) {
    pid, err := pidfile.Read(path)
    return pid, err == nil && pid != 0
}

Try / catch

if err := pidfile.Write(path, os.Getpid()); err != nil {
    if strings.Contains(err.Error(), "is still running") {
        // single-instance lock held: exit cleanly, do not delete the file
        return err
    }
    return err
}

Prevention

When it happens

Trigger: Starting a second instance while the first is running: same --pidfile path, previous process still alive. Also when the old process died but its PID was reused by an unrelated live process (PID-reuse race).

Common situations: docker compose up twice in parallel with the same pidfile; a crashed run left the file and the PID got recycled; daemons/systemd restarting a service before the old one fully exited; stale pidfiles on hosts with fast PID cycling (containers).

Related errors


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