Postgres Change User Password: ALTER USER Guide
Chat2DB Team"Postgres change user password" is one of the most searched PostgreSQL admin tasks, and for good reason: the procedure is short, but the surrounding questions are not. What is the postgres default password? Why does ALTER USER ... WITH PASSWORD show up in server logs? How do you do a postgres reset password when you have locked yourself out of the superuser account? This guide covers the PostgreSQL change password workflow end to end for PostgreSQL 14 through 17, with runnable SQL and shell commands.
The basic way: ALTER USER ... WITH PASSWORD
In PostgreSQL, users and roles are the same object. ALTER USER is an alias for ALTER ROLE, so both of the following statements are equivalent:
ALTER USER app_user WITH PASSWORD 'N3w-Str0ng-P@ss';
ALTER ROLE app_user WITH PASSWORD 'N3w-Str0ng-P@ss';The statement needs one of the following privileges:
- You are changing your own password (any role can do that).
- You are a superuser.
- You have
CREATEROLEand, on PostgreSQL 16 and later,ADMIN OPTIONon the target role. On 15 and earlier,CREATEROLEalone is enough for non-superuser targets.
You can verify that the new password works without leaving psql by opening a second connection:
psql "host=localhost dbname=appdb user=app_user password=N3w-Str0ng-P@ss" -c "select current_user, now();" current_user | now
--------------+-------------------------------
app_user | 2026-08-22 10:14:05.221874+00
(1 row)The change takes effect immediately for new connections. Existing sessions are not disconnected, which matters later when we talk about rotating application credentials.
The downside of plaintext in SQL
ALTER USER ... WITH PASSWORD 'literal' sends the cleartext password to the server inside the SQL text. That means it can be captured in three places:
~/.psql_historyon the client machine.- The server log, if
log_statementisddlorall, or if the statement is slow enough to triplog_min_duration_statement. - Anything in between: a proxy like PgBouncer with verbose logging, an unencrypted connection, or your SQL client's query history.
PostgreSQL does not redact the password from the logged statement. The fix is the next section.
The safer way: the psql \password meta-command
psql ships a meta-command that prompts for the password, hashes it on the client side with the server's password_encryption algorithm, and then sends only the hash inside an ALTER ROLE:
psql -U postgres -d postgrespostgres=# \password app_user
Enter new password for user "app_user":
Enter it again:
postgres=#Omit the role name to change your own password. Because psql does the hashing, the cleartext never reaches the server, never appears in log_statement output, and meta-commands are not written to .psql_history as SQL. The statement that the server actually receives looks like this:
ALTER USER app_user PASSWORD 'SCRAM-SHA-256$4096:...$...:...'PostgreSQL accepts pre-hashed values: if the string already has the SCRAM or MD5 format, it is stored as-is. That is also how tools like Chat2DB, pgAdmin, and libpq's PQencryptPasswordConn() implement safe password changes. If you prefer a graphical workflow, you can run the same ALTER USER statements in Chat2DB, a free AI-powered SQL client (download at https://chat2db.ai/download (opens in a new tab) or use the web version at https://app.chat2db.ai (opens in a new tab)).
What is the postgres default password?
There is no postgres default password. A fresh PostgreSQL installation creates a superuser role named postgres with no password at all, and how you log in depends on the platform:
- Linux packages (Debian, Ubuntu, RHEL, Fedora):
pg_hba.confusespeerauthentication for local Unix-socket connections, so the OS userpostgresis trusted as the database userpostgres. Log in withsudo -u postgres psql. The role has no password until you set one. - Docker (official
postgresimage): the container refuses to start unless you providePOSTGRES_PASSWORD(or setPOSTGRES_HOST_AUTH_METHOD=trust). The value you pass becomes the password of thepostgressuperuser:
docker run -d --name pg17 -e POSTGRES_PASSWORD=changeme -p 5432:5432 postgres:17
psql "host=localhost user=postgres password=changeme"- Windows EDB installer: the setup wizard prompts you for the superuser password during installation. There is no default; if you forgot what you typed, follow the reset steps below.
- Homebrew on macOS:
brew install postgresql@17creates a superuser named after your macOS login, withtrustauth on local connections. Just runpsql postgres.
If you are seeing password authentication failed for user "postgres" right after installing on Linux, you are almost certainly connecting over TCP (-h localhost), which uses scram-sha-256 or md5 instead of peer. Either connect through the socket or set a password first:
sudo -u postgres psql -c "\password postgres"Postgres reset password when you are locked out
When the superuser password is lost, there is no "forgot password" link. The documented recovery method is to temporarily let local connections in without a password, change it, then revert. The exact steps:
Step 1: Find pg_hba.conf
sudo -u postgres psql -c "SHOW hba_file;" # works only if you can still log in somehow
# otherwise, typical locations:
# Debian/Ubuntu: /etc/postgresql/17/main/pg_hba.conf
# RHEL/Fedora: /var/lib/pgsql/17/data/pg_hba.conf
# Windows: C:\Program Files\PostgreSQL\17\data\pg_hba.conf
# Homebrew: /opt/homebrew/var/postgresql@17/pg_hba.confStep 2: Change the local line to trust
Back up the file, then edit the line that matches your connection. For a Unix socket connection:
# TYPE DATABASE USER ADDRESS METHOD
local all all trustFor a TCP connection from the same machine (Windows has no Unix sockets):
host all all 127.0.0.1/32 trustKeep it as narrow as possible: restrict it to postgres as the user and 127.0.0.1/32 as the address, and never leave it in place longer than the minute it takes.
Step 3: Reload the configuration
pg_hba.conf is re-read on reload; no restart is needed:
# Any one of these:
sudo systemctl reload postgresql # Debian/Ubuntu (reloads all clusters)
sudo systemctl reload postgresql-17 # RHEL/Fedora
sudo -u postgres pg_ctl reload -D /var/lib/pgsql/17/data
pg_ctl reload -D "C:\Program Files\PostgreSQL\17\data" # WindowsIf you can open any connection at all, SELECT pg_reload_conf(); does the same job. On Windows, you can also restart the postgresql-x64-17 service from services.msc.
Step 4: Set a new password
psql -h 127.0.0.1 -U postgres -d postgres\password postgres
-- or, if you accept the plaintext-in-log tradeoff:
ALTER USER postgres WITH PASSWORD 'a-long-random-passphrase';Step 5: Revert pg_hba.conf and reload again
Change trust back to scram-sha-256 (or peer for the local socket line), reload, and confirm that a wrong password is now rejected:
sudo systemctl reload postgresql
psql -h 127.0.0.1 -U postgres -d postgres -c "select 1" # should prompt and fail with a wrong passwordCheck pg_hba_file_rules to confirm the file parsed cleanly after your edit:
SELECT line_number, type, database, user_name, address, auth_method, error
FROM pg_hba_file_rules;Any non-null error means your edit was ignored and the old rules are still in memory.
password_encryption: scram-sha-256 vs md5
How PostgreSQL stores a password is controlled by the password_encryption parameter at the moment the password is set, not at login time.
SHOW password_encryption; password_encryption
---------------------
scram-sha-256Since PostgreSQL 14 the default is scram-sha-256. Earlier versions defaulted to md5, and many upgraded clusters still carry the old value in postgresql.conf. MD5 hashes are a single unsalted round of MD5 over password plus username; SCRAM-SHA-256 is salted, iterated (4096 rounds by default), and is not replayable. PostgreSQL 18 deprecates md5 and prints a warning when you set an MD5 password, so plan the migration now.
To upgrade hashes, you must re-set each password, because the server cannot convert an MD5 hash to SCRAM without knowing the cleartext:
-- 1. Switch the default for new passwords
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();
-- 2. Find roles that still have MD5 hashes
SELECT rolname,
CASE
WHEN rolpassword IS NULL THEN 'no password'
WHEN rolpassword LIKE 'md5%' THEN 'md5'
WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram-sha-256'
END AS hash_type
FROM pg_authid
WHERE rolcanlogin
ORDER BY 1; rolname | hash_type
-----------+---------------
app_user | md5
postgres | scram-sha-256
reporting | md5Re-set each MD5 role with \password role_name, then change the pg_hba.conf method from md5 to scram-sha-256 so MD5 logins are refused. Note that an hba line with method md5 will still accept SCRAM-stored passwords, which lets you migrate roles gradually before tightening the rule.
Checking the stored hash in pg_authid
pg_authid is readable only by superusers; pg_roles and pg_user show ******** in the password column for everyone. To inspect a single role:
SELECT rolname, left(rolpassword, 24) AS hash_prefix, rolvaliduntil
FROM pg_authid
WHERE rolname = 'app_user'; rolname | hash_prefix | rolvaliduntil
----------+--------------------------+---------------
app_user | SCRAM-SHA-256$4096:fQ6k |A SCRAM hash has four parts: the algorithm, the iteration count, the base64 salt, and the StoredKey:ServerKey pair. Nothing in it can be reversed into the password, but treat pg_authid dumps as sensitive because the hash is still enough to impersonate the user to this specific server.
Password expiry with VALID UNTIL and disabling with PASSWORD NULL
VALID UNTIL sets a timestamp after which password authentication stops working for the role. It does not drop existing connections, and it does not affect peer, trust, or certificate authentication.
-- Expire at a fixed date
ALTER ROLE contractor_ro VALID UNTIL '2026-09-30 23:59:59+00';
-- Expire a rotated credential 7 days from now
ALTER ROLE app_user VALID UNTIL (now() + interval '7 days')::text; -- fails: VALID UNTIL needs a literal
-- Use a DO block or psql \gexec to compute the literal instead
SELECT format('ALTER ROLE app_user VALID UNTIL %L', now() + interval '7 days') \gexec
-- Remove the expiry
ALTER ROLE app_user VALID UNTIL 'infinity';VALID UNTIL requires a string literal, which is why the second statement above is shown as an error and the \gexec form is the working pattern.
To disable password login entirely without dropping the role, set the password to NULL:
ALTER ROLE legacy_etl WITH PASSWORD NULL;After this, pg_authid.rolpassword is NULL and every md5 or scram-sha-256 attempt fails, while peer, cert, and gss logins keep working. To block all logins, use ALTER ROLE legacy_etl NOLOGIN instead.
Keeping passwords out of the server log
If you cannot use \password (for example, in an Ansible playbook that runs SQL over a TCP connection), reduce the exposure:
-- For this session only: do not log statements, and do not record a slow-query entry
SET log_statement = 'none';
SET log_min_duration_statement = -1;
ALTER USER app_user WITH PASSWORD 'N3w-Str0ng-P@ss';
RESET log_statement;
RESET log_min_duration_statement;SET log_statement requires superuser (or a role granted SET on it in PostgreSQL 15+). Also check log_min_error_statement: if the ALTER USER fails for any reason, the full statement, password included, goes to the log at ERROR level unless that parameter is raised above ERROR for the session. A safer alternative is to pre-hash in your automation with the SCRAM algorithm (libpq's PQencryptPasswordConn, or scram.ScramClient in Python's scramp library) and pass the hash.
Finally, avoid putting passwords on the psql command line or in PGPASSWORD in shared environments; use ~/.pgpass with 0600 permissions or a connection service file.
Changing passwords on managed PostgreSQL services
Managed providers own pg_hba.conf, so the trust-reset trick does not apply, but the ALTER USER path works for every role you created:
- Amazon RDS / Aurora: the master user's password is changed from the console or CLI, not with
ALTER USER:aws rds modify-db-instance --db-instance-identifier mydb --master-user-password 'NewPass' --apply-immediately. Other roles:ALTER USERas usual. Consider--manage-master-user-passwordto hand rotation to Secrets Manager. - Supabase: the
postgresdatabase password is reset under Project Settings, Database, "Reset database password". Roles you created can be changed withALTER ROLEin the SQL editor. - Neon: each role's password is reset from the console (Roles, "Reset password") or
neon rolesCLI; Neon generates the new value for you.ALTER ROLE ... PASSWORDis also allowed for roles owned by your project. - Azure Database for PostgreSQL / Cloud SQL: admin password via
az postgres flexible-server update --admin-passwordorgcloud sql users set-password; other roles via SQL.
Rotating an application password without downtime
Changing the password while the app is running does not kill open connections, but every reconnect, pool refill, or restart after the change will fail until the app has the new secret. For a zero-error rotation, use two roles and a shared group:
-- One-time setup: privileges live on the group, not on the login roles
CREATE ROLE app_rw NOLOGIN;
GRANT CONNECT ON DATABASE appdb TO app_rw;
GRANT USAGE ON SCHEMA public TO app_rw;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_rw;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_rw;
-- Current credential
CREATE ROLE app_v1 LOGIN PASSWORD 'old-secret' IN ROLE app_rw;Rotation:
-- 1. Create the next credential
CREATE ROLE app_v2 LOGIN PASSWORD 'new-secret' IN ROLE app_rw;
-- 2. Roll the application to app_v2 (config change, rolling restart)
-- 3. Confirm nothing still uses app_v1
SELECT count(*) FROM pg_stat_activity WHERE usename = 'app_v1';
-- 4. Retire the old credential
ALTER ROLE app_v1 NOLOGIN;
-- later: DROP ROLE app_v1;If objects are owned by the login role rather than the group, run REASSIGN OWNED BY app_v1 TO app_rw; before dropping. If you must keep a single role name, set VALID UNTIL a few minutes ahead on the old hash, push the new password to the app, then ALTER USER and SELECT pg_terminate_backend(pid) for any straggling sessions.
Summary
ALTER USER name WITH PASSWORD '...'is the PostgreSQL change password statement;ALTER ROLEis identical.- Prefer psql's
\password: it hashes client-side, so the plaintext never hits server logs or history. - There is no postgres default password. Linux uses
peerauth for the localpostgresuser, Docker requiresPOSTGRES_PASSWORD, the Windows installer prompts, Homebrew usestrust. - Postgres reset password for a lost superuser: set
pg_hba.conftotrustfor a local line, reload,\password postgres, revert, reload. - Use
scram-sha-256; MD5 hashes must be re-set to migrate. Checkpg_authid.rolpasswordto see which roles still use MD5. VALID UNTILexpires a password;PASSWORD NULLdisables password login;NOLOGINblocks all logins.- For application credentials, rotate by creating a second login role under a shared group instead of changing a live password.
FAQ
Why does ALTER USER succeed but the app still cannot log in?
The most common causes are a pg_hba.conf line that does not match the app's host or database, a pooler like PgBouncer that has its own userlist.txt with the old hash, or a hash type mismatch (an md5 hba line with a client library that cannot do SCRAM, or vice versa). Check pg_hba_file_rules, then look at the server log for the exact rejection reason.
Is there a postgres default password in Docker?
No. The official image requires POSTGRES_PASSWORD, or you must opt in to POSTGRES_HOST_AUTH_METHOD=trust. The value is applied only on the first start when the data directory is empty; changing the environment variable later does not change the password. Use ALTER USER postgres WITH PASSWORD inside the running container instead.
Does changing a password disconnect existing sessions?
No. PostgreSQL checks the password only at connection time. Open sessions keep working until they disconnect. To force a re-authentication, terminate them with SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename = 'app_user';.
