How to list databases in PostgreSQL
List every PostgreSQL database with \l in psql, or with a pg_database query from SQL, your app or a GUI. Includes sizes, owners and scripting.
Quick Answer
In psql, run \l (or \list):
\l
From SQL, for example in an app or a GUI, query the pg_database catalog:
SELECT datname FROM pg_database WHERE NOT datistemplate ORDER BY datname;
List databases in psql
\l prints one row per database with its owner, encoding, collation and access privileges. It works from any database you're connected to, because the database list is shared by the whole server.
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | ...
-----------+----------+----------+-----------------+------------+----
myapp | postgres | UTF8 | libc | en_US.utf8 |
postgres | postgres | UTF8 | libc | en_US.utf8 |
template0 | postgres | UTF8 | libc | en_US.utf8 |
template1 | postgres | UTF8 | libc | en_US.utf8 |
| Command | What it adds |
|---|---|
\l | Name, owner, encoding, collation, access privileges |
\l+ | Also size on disk, tablespace and description |
\l app* | Only databases whose names match the pattern |
You can also list databases without opening a session. psql -l connects, prints the list and exits:
psql -h localhost -U postgres -l
List databases with a SQL query
\l is a psql shortcut, so it fails in pgAdmin, DBeaver, application code and most other clients. Use the pg_database system catalog there instead. \l reads the same catalog behind the scenes.
-- names only, without template0 and template1
SELECT datname
FROM pg_database
WHERE NOT datistemplate
ORDER BY datname;
-- with owner and encoding, like \l
SELECT d.datname,
pg_get_userbyid(d.datdba) AS owner,
pg_encoding_to_char(d.encoding) AS encoding
FROM pg_database d
WHERE NOT d.datistemplate
ORDER BY 1;
PostgreSQL has no SHOW DATABASES. That's MySQL syntax. In PostgreSQL, SHOW reads configuration settings, so it fails with unrecognized configuration parameter "databases".
List databases by size
To find which database is using the disk, sort by pg_database_size():
SELECT datname,
CASE WHEN has_database_privilege(datname, 'CONNECT')
THEN pg_size_pretty(pg_database_size(datname))
ELSE 'no access' END AS size
FROM pg_database
WHERE datallowconn
ORDER BY CASE WHEN has_database_privilege(datname, 'CONNECT')
THEN pg_database_size(datname) END DESC NULLS LAST;
datallowconn leaves out template0, which doesn't accept connections. The CASE matters for anyone who isn't a superuser: without it, one database you can't connect to makes the whole query fail (see below).
What we found testing this
- The usual size query breaks for non-superusers. We revoked
CONNECTon one database and ran the commonpg_database_size(datname)query as an ordinary role. It returned no rows at all, onlyERROR: permission denied for database learn_private.\l+handles this by printing No Access in the Size column. The query above does the same. To let a monitoring role see every size without connect rights, grant itpg_read_all_stats, which the size function also accepts (PostgreSQL docs). - An empty database still takes about 7 MB. A brand-new database with no tables measured
7425 kBon PostgreSQL 16, because it's a full copy oftemplate1and its system catalogs. A hundred per-tenant databases cost roughly 725 MB before any data goes in. - The Access privileges column is how to spot a locked database. Empty means the defaults, where everyone may connect. After the revoke, it read
=T/postgres: PUBLIC (the part before=is empty) keeps onlyT, temporary tables, and no longer hasc, connect. psql -Eshows the query behind any meta-command. Runpsql -E -c "\l"and psql prints thepg_databasequery it ran. The columns change between versions (PostgreSQL 16 added ICU Rules), so copying psql's own query for your version beats guessing.
What postgres, template0 and template1 are
A fresh server always has these three, and none of them is your data:
postgresis an empty default database, so tools and admins have somewhere to connect. It's safe to keep and use for admin work.template1is copied every time you runCREATE DATABASE. Anything you add to it, such as an extension, appears in every new database.template0is a pristine copy that can't be changed. Use it withCREATE DATABASE ... TEMPLATE template0when you need a different encoding or locale.
Don't drop them. Filter them out with WHERE NOT datistemplate instead.
Use the list in a shell script
psql -l output is padded and wrapped, so it's awkward to parse. For scripts, run the query in unaligned, tuples-only mode with -At, which prints one bare name per line:
for db in $(psql -U postgres -Atc "SELECT datname FROM pg_database WHERE NOT datistemplate"); do
echo "backing up $db"
pg_dump -U postgres -Fc "$db" > "$db.dump"
done
Which database you are connected to
Listing databases doesn't switch to one. Check where you are and move with \c:
SELECT current_database();
\c myapp
You'll see every database in the list, even ones you can't open. has_database_privilege(datname, 'CONNECT') tells you which ones you can.
Frequently Asked Questions
What is the PostgreSQL equivalent of SHOW DATABASES?
In psql it is \l. In SQL, run SELECT datname FROM pg_database WHERE NOT datistemplate. PostgreSQL has no SHOW DATABASES statement.
How do I list databases without logging into psql?
Run psql -l with your usual connection flags, for example psql -h localhost -U postgres -l. It prints the list and exits.
Why can I see databases I cannot connect to?
pg_database is readable by every role, so the list shows all databases. Whether you can open one depends on the CONNECT privilege, which has_database_privilege() checks.
How do I see the size of each database?
Use \l+ in psql, or the size query on this page. Wrap pg_database_size() in a has_database_privilege() check, or one database you cannot connect to makes the whole query fail.