Fix PostgreSQL Server Won't Start on Mac OS X (2026)
A fresh Homebrew install of PostgreSQL on a Mac completes without errors, then psql answers connection refused because the server was never started. Which command starts it depends on
"How can I start PostgreSQL server on Mac OS X" — the exact commands that work#
A fresh Homebrew install of PostgreSQL on a Mac completes without errors, and then the first psql attempt answers "connection refused." Running pg_ctl start throws back "pg_ctl: no database directory specified and no PGDATA environment variable." This is the exact scenario in the original Stack Overflow question, and the answers diverge because the right command depends on how PostgreSQL was installed. The fix is straightforward once you know which init system your installation uses: if you installed with Homebrew, use brew services start postgresql@16. If you used Postgres.app, start it from the Applications folder. If you built from source or used the EnterpriseDB installer, you need pg_ctl with the -D flag pointing at your data directory. The sections below cover each variant so you can pick the one that matches your setup.
What you'll see#
When PostgreSQL isn't running, you'll encounter one of these symptoms depending on what you try:
Trying to connect with psql:
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory
Is the server running locally and accepting connections on that socket?Trying to start manually with pg_ctl:
pg_ctl: no database directory specified and no PGDATA environment variable
Try "pg_ctl --help" for more information.Or, if you try brew services list and see:
postgresql@16 noneIt happens right after a fresh install, after a macOS upgrade, or after a reboot if the service wasn't configured to launch at startup. The behavior is the same across macOS Ventura, Sonoma, and Sequoia — the underlying issue is always that the PostgreSQL server process isn't running.
Why PostgreSQL doesn't auto-start on macOS#
macOS doesn't know you installed PostgreSQL. Unlike Linux distributions that wire up systemd units during package installation, Homebrew on macOS gives you the binaries and leaves the service management to you. There are three layers at play here, and missing any one of them means PostgreSQL stays dead.
First, PostgreSQL itself needs an initialized data directory. Homebrew runs initdb for you during installation, creating the cluster at /opt/homebrew/var/postgresql@16 on Apple Silicon or /usr/local/var/postgresql@16 on Intel Macs. If you installed through a different method — the EnterpriseDB graphical installer, Postgres.app, or compiling from source — that directory lives somewhere else, and PostgreSQL has no way to guess where.
Second, macOS uses launchd as its service manager. Homebrew integrates with launchd through brew services, which writes a .plist file to ~/Library/LaunchAgents/. That plist tells launchd what binary to run, what arguments to pass, and whether to restart on failure. If you never ran brew services start, that plist either doesn't exist or exists but isn't loaded.
Third, the PGDATA environment variable is not set by default. When you run pg_ctl start without -D, PostgreSQL looks for PGDATA in your shell environment. It's not there unless you added it to your .zshrc or .bash_profile manually. That's why the "no database directory specified" error is so common — it's not a bug, it's PostgreSQL telling you it needs one piece of information you haven't given it.
The fix: start PostgreSQL with the method that matches your install#
The command you run depends entirely on how PostgreSQL got onto your Mac. Here's the decision tree.
If you installed with Homebrew (most common)#
Find your installed version first:
brew list --versions | grep postgresqlYou'll see something like postgresql@16 16.4. Then start the service:
brew services start postgresql@16Expected output:
==> Successfully started `postgresql@16` (label: homebrew.mxcl.postgresql@16)To have it start automatically on every reboot, that single command is enough — brew services writes the launchd plist and loads it immediately. No separate enable step is needed.
If you used Postgres.app#
Postgres.app is a self-contained application bundle. Move it to /Applications and open it; on the Postgres.app home page the first launch is "Click "Initialize" to create a new server", and the app is then controlled from its menu bar item. The app does not put psql on your $PATH by itself: its installation guide has you run sudo mkdir -p /etc/paths.d && echo /Applications/Postgres.app/Contents/Versions/latest/bin | sudo tee /etc/paths.d/postgresapp, then open a new terminal window.
If you installed via the EnterpriseDB installer or compiled from source#
You need to know where your data directory is. Common locations:
- EnterpriseDB:
/Library/PostgreSQL/16/data - Source build:
/usr/local/pgsql/data, the directory used in the installation guide's short version
These installers do not put their bin directory on your PATH (the EDB macOS guide has you add /Library/PostgreSQL/<version>/bin yourself; the source build installs to /usr/local/pgsql/bin), so call pg_ctl by its full path:
/Library/PostgreSQL/16/bin/pg_ctl -D /Library/PostgreSQL/16/data startIf you get a permissions error, the data directory is owned by the postgres user that the installer created. Switch to that user:
sudo -u postgres /Library/PostgreSQL/16/bin/pg_ctl -D /Library/PostgreSQL/16/data startIf you don't know where your data directory is#
Search for it:
sudo find / -name "pg_hba.conf" 2>/dev/nullThe directory containing pg_hba.conf is your data directory. For Homebrew's postgresql@16 on Apple Silicon, whose cluster the formula creates under $HOMEBREW_PREFIX/var, the match is:
/opt/homebrew/var/postgresql@16/pg_hba.confSo the data directory is /opt/homebrew/var/postgresql@16.
Verify the fix#
Homebrew's versioned formulae are keg-only ("keg_only": true in the postgresql@16 formula data), so pg_isready, psql, pg_ctl and initdb are not on your PATH after brew install. Add them for the current shell first (and to ~/.zshrc to keep them):
export PATH="$(brew --prefix postgresql@16)/bin:$PATH"Then run pg_isready — it's the simplest check and ships with every PostgreSQL installation:
pg_isreadyIt prints the socket directory (or host) and port, then the state. The pg_isready documentation shows a running server as:
/tmp:5432 - accepting connectionsWhen nothing is listening, the state reads no response instead, and the command exits with status 2.
For Homebrew specifically, confirm the service status:
brew services listLook for your PostgreSQL version in the output:
postgresql@16 started mahdi ~/Library/LaunchAgents/homebrew.mxcl.postgresql@16.plistThe "started" status means launchd has the process running and will restart it if it crashes. "none" means the service isn't loaded at all. "error" means launchd tried to start it but the process exited with a non-zero code, which brew services list prints right after the word error. brew services info --verbose postgresql@16 shows the log file path (plain info does not), and that log holds the reason.
Finally, connect with psql to confirm you can actually run queries:
psql -d postgres -c "SELECT version();"Do not add -U postgres on a Homebrew install. The formula initialises the cluster with initdb --locale=en_US.UTF-8 -E UTF-8 $HOMEBREW_PREFIX/var/postgresql@16, without -U, and the initdb documentation says the superuser name defaults to the operating-system user running initdb — your macOS username, so a postgres role does not exist unless you create it. The query returns one row whose text starts with PostgreSQL 16. followed by the platform it was built for.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Two variants that still trip people up#
Variant A — "Is the server running?" after a macOS upgrade#
After a major macOS upgrade, brew services list can show your PostgreSQL service as "started" while pg_isready says "no response". Restart the service first:
brew services restart postgresql@16If that still fails, read the server log. The formula's service definition writes it to $HOMEBREW_PREFIX/var/log/postgresql@16.log (/opt/homebrew/var/log/postgresql@16.log on Apple Silicon, /usr/local/var/log/postgresql@16.log on Intel). If the log reports a permission error on the data directory, give it back to your user:
sudo chown -R $(whoami) /opt/homebrew/var/postgresql@16Variant B — Another server already on port 5432, or a stale postmaster.pid#
pg_ctl reports both cases the same way:
pg_ctl: could not start server
Examine the log output.The log tells them apart. Both were reproduced on 19 October 2026 with PostgreSQL 16.15 in the official postgres:16-alpine image.
A second server on the same port. If Postgres.app and Homebrew's PostgreSQL are both installed, or an old postgresql@15 is still running after an upgrade to 16, the new server cannot bind the port. Starting a second cluster on 5432 next to a running one logged:
2026-10-19 02:35:19.852 UTC [75] HINT: Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
2026-10-19 02:35:19.852 UTC [75] FATAL: could not create any TCP/IP sockets(On Linux musl the preceding could not bind line ends in Address in use; macOS words the same error Address already in use.) Find what holds the port, then stop it:
lsof -i :5432
brew services stop postgresql@15 # if you upgraded from 15 to 16A postmaster.pid left behind. The lock file in the data directory records the PID of the server that wrote it. PostgreSQL only refuses to start when that PID belongs to a live process — after a crash, macOS may have given the same PID to an unrelated program. With a postmaster.pid naming a running sleep process, the start failed with:
2026-10-19 02:35:20.157 UTC [91] FATAL: lock file "postmaster.pid" already exists
2026-10-19 02:35:20.157 UTC [91] HINT: Is another postmaster (PID 77) running in data directory "/tmp/d2"?Once that PID was dead, the same file was ignored and the server started normally. So check the PID from the hint with ps -p <PID>: if it is a postgres process, a server really is running on that data directory — stop it with pg_ctl -D <datadir> stop. If it is some other program, and ps aux | grep postgres shows no server for that directory, delete <datadir>/postmaster.pid and start again.
If you need two versions running at once, give each its own port in the postgresql.conf of its data directory (connection settings): every Homebrew version defaults to 5432.
Keep PostgreSQL running across reboots#
The brew services start command handles this automatically — it writes a launchd plist that loads at login. But if you ever need to check or manually manage the plist, it lives at:
cat ~/Library/LaunchAgents/homebrew.mxcl.postgresql@16.plistThe key fields are RunAtLoad (set to true so it starts when you log in) and KeepAlive (set to true so launchd restarts it if it crashes). You can disable auto-start without uninstalling:
brew services stop postgresql@16According to the Homebrew manpage, stop stops the service "immediately and unregister[s] it from launching at login (or boot), unless --keep is specified". Use brew services stop --keep postgresql@16 to stop it now but keep it starting at login. To re-enable:
brew services start postgresql@16brew services cleanup ("Remove all unused services" in the manpage) deletes every Homebrew plist in ~/Library/LaunchAgents whose service is not currently running, and kills running services whose plist is gone — per its source. A service you stopped with --keep loses its plist too.
For manual pg_ctl users, you can write your own launchd plist or add an alias to your shell config. An alias is the more explicit option, because PostgreSQL then starts only when you run it:
alias pgstart='pg_ctl -D /opt/homebrew/var/postgresql@16 -l ~/pg.log start'
alias pgstop='pg_ctl -D /opt/homebrew/var/postgresql@16 stop'Add those to ~/.zshrc, then source ~/.zshrc. Now pgstart and pgstop work from any terminal.
FAQ#
How do I start PostgreSQL after installing it with Homebrew?#
Run brew services start postgresql@16 (replace 16 with your installed version — check with brew list --versions | grep postgresql). This uses launchd to start PostgreSQL and keep it running across reboots. Verify with brew services list to confirm the service status shows "started." Homebrew initialises the database cluster during installation. If that directory is missing, recreate it with the command from the formula's caveat, initdb --locale=en_US.UTF-8 -E UTF-8 $(brew --prefix)/var/postgresql@16; initdb refuses a directory that is not empty ("exists but is not empty"), so it never overwrites an existing cluster.
Why does pg_ctl say "no database directory specified"?#
PostgreSQL needs to know where its data directory lives. If you installed via Homebrew, the data directory is typically at /opt/homebrew/var/postgresql@16 on Apple Silicon or /usr/local/var/postgresql@16 on Intel Macs. Set the PGDATA environment variable in your shell config (export PGDATA=/opt/homebrew/var/postgresql@16 in ~/.zshrc) or pass -D /path/to/data explicitly to pg_ctl. If you're unsure where your data directory is, search for pg_hba.conf — the directory containing that file is your data directory.
How do I check which version of PostgreSQL I'm running?#
Run psql --version from the terminal or connect and run SELECT version();. If psql isn't found, you may need to install the client tools first, or add the keg-only bin directory to your PATH — see Fix: psql: command not found. For a deeper dive into version checking methods, including how to query the server version remotely, see Which version of PostgreSQL am I running?.
Can I have multiple PostgreSQL versions installed on the same Mac?#
Yes. Homebrew supports this with versioned formulae (postgresql@14, postgresql@15, postgresql@16). Each version gets its own data directory, but every one of them defaults to port 5432, so only one can run until you set a different port in the second version's postgresql.conf. Then start them independently with brew services start postgresql@14 and brew services start postgresql@16, and connect to a specific version by port, for example psql -p 5433 -d postgres. If you need to change a user's password across versions, the exact ALTER ROLE syntax is in How to Change a PostgreSQL User Password.
Further reading: PostgreSQL 18 Docker: Fix Existing Volume Startup Errors.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
PostgreSQL 18 Docker: Fix Existing Volume Startup Errors
Separate a Docker mount failure from a PostgreSQL major upgrade, preserve the original volume and prove the restored database survives a restart.
How to Change a PostgreSQL User Password (Supabase)
`ALTER ROLE alice WITH PASSWORD 'newpass';` is the SQL. The psql `\password` prompt avoids logging the cleartext. In Supabase the `postgres` role password is reset from the Dashboard, not SQL. Here is each method, the scram-sha-256 default, and the three things that break after a password change.
Fix Deno 'UnsupportedCertVersion' TLS Error on Supabase
Deno Deploy throws 'UnsupportedCertVersion' TLS error when connecting to Supabase Postgres. Here's how to use --unsafely-ignore-certificate-errors and verify your connection stays secure.


