Troubleshooting

Shared Memory Troubleshooting

A PostgreSQL No space left on device message can mean exhausted operating system IPC capacity rather than a full disk; identify the failed system call before changing configuration or removing resources.

Identify the Failure

Read the complete PostgreSQL diagnostic, including DETAIL and HINT:

Diagnostic Resource First check
could not create shared memory segment with shmget(...) System V shared memory Segment count and stale postmasters
could not create semaphores with semget(...) System V semaphores Semaphore limits and server concurrency
could not resize shared memory segment "/PostgreSQL..." Dynamic shared memory The configured dynamic implementation and its backing capacity
could not extend file or another named filesystem path Disk storage Free blocks and inodes on that filesystem

The automatic recovery described below applies only to the first case on macOS. Settings such as shared_buffers, max_connections, and max_locks_per_transaction do not free a System V segment ID left by a dead server.

Automatic macOS Recovery

PostgreSQL records its System V shared-memory key and segment ID in postmaster.pid. When that file names a dead postmaster, local-postgres can remove the recorded segment before continuing:

Lifecycle path Recovery point
startPostgres and startPostgresDataDir Before starting an existing data directory
Their returned stop() operations After the managed postmaster exits
stopPostgresDataDir When the recorded or expected postmaster is already dead or exits during shutdown
local-postgres/tmp Through the same core lifecycle, including its detached cleanup worker

There is no option to enable. On macOS, recovery runs automatically and invokes /usr/bin/ipcs and /usr/bin/ipcrm directly without a shell.

Before removing anything, local-postgres requires all of these facts to agree:

  • postmaster.pid belongs to the current user and records the requested data directory
  • the recorded postmaster PID no longer exists
  • the kernel entry has the recorded key, segment ID, and creator PID
  • the kernel entry's owner and creator are the current user
  • no process is attached to the segment
  • an expectedPid, when supplied, matches the recorded postmaster generation

Any missing field, conflicting value, attached process, unexpected command output, or command failure preserves the segment. The lifecycle then continues; a later startup may report PostgreSQL's original error. When a logger is configured, successful recovery produces a message like:

[postgres] Removed stale shared memory segment 19529730 for .postgres.

Know the Recovery Boundary

Automatic recovery is intentionally not a user-wide IPC sweep. It cannot remove a segment when:

  • its data directory or postmaster.pid was already deleted
  • postmaster.pid is malformed or does not contain the segment identity
  • an unrelated stale segment exhausted the system-wide limit
  • a new cluster fails inside initdb, before it has durable postmaster identity for local-postgres to verify
  • another process remains attached
  • the host is Linux, Windows, or another non-macOS platform

These limits prevent one cluster lifecycle from deleting IPC resources owned by another PostgreSQL installation or application.

Inspect macOS System V Capacity

Check the system-wide segment limit and list the current segments:

sysctl kern.sysv.shmmni
ipcs -m -p
ipcs -m -o

Join the two ipcs views by segment ID. CPID is the creator PID and NATTCH is the number of attached processes. Compare a cluster's recorded PID, key, and segment ID without modifying anything:

sed -n -e '1p' -e '7p' .postgres/postmaster.pid
ps -p "$(head -n 1 .postgres/postmaster.pid)" -o pid=,command=

An absent creator PID and NATTCH=0 are necessary evidence of a stale segment, but they do not by themselves prove that the segment belongs to this package. Retain the data-directory and postmaster.pid match as the ownership boundary.

Recover When Automatic Cleanup Cannot

Prefer these remedies in order:

  1. Restart the same cluster through startPostgres. An intact postmaster.pid lets the package apply its exact-match recovery.

  2. Reboot the development machine when the original data directories are gone or the remaining segments cannot be attributed safely. Rebooting clears stale System V IPC state.

  3. Remove one exact segment manually only after verifying its current-user ownership, dead creator PID, zero attachments, and PostgreSQL provenance:

    ipcrm -m <SHMID>

    Removal cannot be undone, although a genuinely stale shared-memory segment contains no database files. Never remove an attached segment or run a broad user-wide IPC cleanup while PostgreSQL or another IPC-using application is active.

  4. Increase the operating system's SHMMNI limit when the machine intentionally runs more concurrent PostgreSQL servers than the current limit permits. This is an administrator-level, machine-wide change and may require a reboot depending on the macOS version.

Diagnose Dynamic Shared-Memory Resize Failures

An error containing could not resize shared memory segment "/PostgreSQL..." does not use the stale System V recovery above. Check PostgreSQL's selected implementation:

SHOW dynamic_shared_memory_type;

On Linux, a posix implementation is commonly backed by /dev/shm; inspect it even when PostgreSQL runs directly rather than in Docker:

df -h /dev/shm

For development clusters where POSIX shared memory is unavailable or too constrained, callers can initialize a new cluster with memory-mapped files in its data directory:

await startPostgres({
  dataDir: '.postgres',
  config: {
    dynamic_shared_memory_type: 'mmap',
  },
})

config applies only when a cluster is first initialized. PostgreSQL generally discourages mmap for production because modified pages may cause additional disk I/O. See PostgreSQL's documentation for: