Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

By updated

Improve this page

PostgreSQL logo

A PostgreSQL instance that is running is not necessarily a usable one. Three obstacles stand between any service and the server: the server must listen on the right address, it must accept the client, and the client must be able to authenticate without anyone typing a password. This lesson clears them one by one with psql, the official command-line client, then teaches you to read the real state of the server instead of assuming it.

What you will learn

  • Connect to an instance over the Unix socket, then over TCP
  • Open network access: listen_addresses on one side, a host line in pg_hba.conf on the other
  • Set up a ~/.pgpass file that psql really honours, and prove it
  • Create a schema and a table owned by the service role
  • Read the real state of the server with SHOW, pg_settings and the catalog

Prerequisites

This lesson administers an instance, it does not teach the SQL language. It therefore assumes:

  • a PostgreSQL 18 instance installed and running, as in the Installation guide;
  • the architecture vocabulary (cluster, database, schema, role), covered in Découvrir PostgreSQL (in French);
  • the basics of SQL and of psql in scripts, covered in the SQL course: Lire le schéma d'une base PostgreSQL (in French) and psql en script (in French). The \copy import, the -v and \gset variables, exit codes and transactions are explained there, this lesson does not repeat them.

Quick check:

Terminal window
psql --version
psql (PostgreSQL) 18.3 (Debian 18.3-1.pgdg12+1)

Why is a running instance still unreachable?

A remote client goes through three successive doors, and each lives in a different place. The first is listen_addresses, in postgresql.conf: until the server's address is listed there, nobody reaches port 5432. The second is pg_hba.conf, which says who gets in, from where and with which method. The third is on the client side: a script cannot type a password, so it needs a .pgpass readable by itself alone.

The diagram below follows a remote client from left to right. Look at where each failure stops and which action fixes it: a door closed by listen_addresses requires a restart, whereas a rejection by pg_hba.conf is settled with a simple reload.

A remote client goes through three doors: if listen_addresses does not listen on the address, the connection is refused and a restart is needed; if no host line of pg_hba.conf matches, the server rejects the client and a reload is enough; if the password is not available without typing, psql waits for an answer and a script hangs; with a .pgpass in 0600, the session opens.noyesnoyesnoyes, .pgpass in 0600Remote clientpsql -h ‹address›listen_addresseslistens on this address?pg_hba.confdoes a host line match?Password availablewithout typing?Connection refusedfix: restartRejected by the serverfix: reloadpsql waits for inputa script hangsSession open
Three doors, three files: listen_addresses, pg_hba.conf, then .pgpass on the client side. Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

How do you connect over the socket and over TCP?

psql connects through two channels, and the channel decides the authentication method. The Unix socket stays on the server's machine; TCP crosses the network, even toward 127.0.0.1. PostgreSQL also distinguishes the system user from the database role: these are two separate notions, even when they share a name.

Over the Unix socket: peer authentication

On the server itself, the peer method compares the system user with the PostgreSQL role, with no password:

Terminal window
sudo -u postgres psql

The postgres=# prompt appears. The # indicates a superuser; an ordinary role would see =>. To target a specific database:

Terminal window
sudo -u postgres psql -d lab_admin

Over TCP: host, port, role and database

A TCP connection names the host, the port, the role and the database explicitly. It no longer goes through peer but through the method that pg_hba.conf declares for that address, usually scram-sha-256, which asks for a password:

Terminal window
psql -h 127.0.0.1 -p 5432 -U admin_lab -d lab_admin

psql also accepts a full URI, handy in an environment variable: postgresql://admin_lab@127.0.0.1:5432/lab_admin. The format is postgresql://[user[:password]@][host][:port][/dbname][?param=value].

Environment variables and the service file

Repeating -h, -p, -U and -d on every call is tedious and error-prone. Two mechanisms factor them out. The environment variables PGHOST, PGPORT, PGUSER and PGDATABASE are read by psql when the matching option is absent:

Terminal window
export PGHOST=127.0.0.1
export PGPORT=5432
export PGUSER=admin_lab
export PGDATABASE=lab_admin
psql

The service file ~/.pg_service.conf names a set of connection parameters, in INI format. You then call it by name, which avoids scattering hosts and ports across scripts:

[facturation]
host=192.168.122.60
port=5432
dbname=facturation
user=appli
Terminal window
psql "service=facturation"

The file is read by default from the account's home directory (~/.pg_service.conf); the PGSERVICEFILE variable points to another one, and PGSERVICE=facturation even spares you from naming the service on the command line. Write each key glued to the = sign: with host = 192.168.122.60, psql answers psql: error: syntax error in service file "/home/bob/.pg_service.conf", line 2. An unknown service name gives psql: error: definition of service "nexiste" not found. A parameter added on the line ("service=facturation dbname=autre") overrides the one in the file.

The password has no place in this file: psql fetches it from ~/.pgpass, which recognises the host given by the service. This lesson's lab does not exercise the service file; the technique is mostly useful when one administration workstation targets several instances.

The .pgpass file: a password without typing

A script cannot type a password. PostgreSQL provides the ~/.pgpass file for that, one line per connection in the format host:port:database:role:password. Create it with the right mode before writing the secret into it, then edit it:

Terminal window
install -m 0600 /dev/null ~/.pgpass
nano ~/.pgpass
192.168.122.60:5432:facturation:appli:<change-me>

The host field must match literally what you pass to -h: a localhost:... line does not serve for -h 192.168.122.60, and psql then reports nothing: the failure looks like that of a file with overly broad permissions, described in the box below. The wildcard * stands for the host, the port, the database or the role (*:*:*:appli:password works).

The only real proof is a connection that succeeds without a password being supplied. The -w option (--no-password) forbids psql from asking for anything: it succeeds or it fails, it never blocks. A closed standard input guarantees that no human answers in its place:

Terminal window
psql -w -h 192.168.122.60 -U appli -d facturation -At -c 'select 1' < /dev/null

The command must answer 1. For exit codes and the other ways of passing a secret, the lesson psql en script (in French) takes over.

\conninfo: check where you are

Once connected, \conninfo tells you which database, with which role, and through which channel you are talking:

\conninfo
Parameter | Value
----------------------+---------------------
Database | lab_admin
Client User | postgres
Socket Directory | /var/run/postgresql
Server Port | 5432
Protocol Version | 3.0
Password Used | false
SSL Connection | false
Superuser | on

Three lines are read first: Database (the active database), Password Used (false means peer authentication through the socket) and SSL Connection (false: no encryption). The Socket Directory line appears only for a connection through a socket.

How do you open network access to the instance?

Opening network access takes two files, because each answers a different question. listen_addresses in postgresql.conf decides on which addresses the server listens; pg_hba.conf decides whom it accepts. Setting one without the other leaves the connection failing, with a different message each time.

On Debian, the instance as shipped listens only on localhost. For it to listen on its private address too, edit /etc/postgresql/18/main/postgresql.conf:

listen_addresses = 'localhost,192.168.122.60'

listen_addresses is a parameter with the postmaster context: it only takes effect on restart of the service, never live. The lesson Configurer PostgreSQL (in French) explains why and how to know in advance.

Terminal window
sudo systemctl restart postgresql@18-main
ss -ltn sport = :5432

The second command lists the listeners on port 5432: you must see an address other than the loopback. You still have to tell the server whom to accept, with a host line added to /etc/postgresql/18/main/pg_hba.conf:

host facturation appli 192.168.122.0/24 scram-sha-256

The columns read in order: connection type, database, role, client address, method. The server applies the first line that matches. The scram-sha-256 method never carries the password in clear; password sends it as is and trust lets anyone in without asking, which is worse than the starting state. This file is reloaded without cutting anyone off:

Terminal window
sudo systemctl reload postgresql@18-main

The pg_hba_file_rules view shows how the server read the file and flags the lines in error. Full hardening of pg_hba.conf, TLS included, is covered in Sécuriser PostgreSQL (in French).

How do you read the real state of the server?

An administrator does not guess an instance's configuration, they read it. Three sources complement each other: SHOW for a single value, the pg_settings view for all values with their origin, and the catalog (pg_database, pg_tables, pg_roles) for objects. The psql meta-commands are only a shortcut to that catalog.

SHOW and pg_settings: the parameters in service

SHOW answers for one parameter, the pg_settings view for all of them, with the source of the value and its context (which says how it can be changed):

SHOW server_version;
SHOW listen_addresses;
SHOW data_checksums;
SELECT name, setting, source, context
FROM pg_settings
WHERE name IN ('server_version', 'listen_addresses', 'data_checksums');

data_checksums says whether the instance protects its data pages with a checksum. Remember that pg_settings gives the value the server is running with, not what a file contains: the lesson Configurer PostgreSQL (in French) builds on that difference.

Meta-commands that query the catalog

Meta-commands start with \ and are interpreted by psql itself, not by the server. They follow a constant logic: d for describe, followed by a letter that designates the object type (n schema, t table, i index, u role). The + suffix adds details.

\l
Name | Owner | Encoding | Locale Provider | Collate | Ctype | Access privileges
-----------+-----------+----------+-----------------+---------+---------+-----------------------
lab_admin | admin_lab | UTF8 | libc | C.UTF-8 | C.UTF-8 |
postgres | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 |
template0 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | =c/postgres +
| | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | =c/postgres +
| | | | | | postgres=CTc/postgres

\l lists the databases and their owner. \du does the same for roles, and \dt app.* for the tables of a schema:

\du
Role name | Attributes
-----------+------------------------------------------------------------
admin_lab | Create DB
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
\dt app.*
Schema | Name | Type | Owner
--------+-----------+-------+----------
app | clients | table | postgres
app | commandes | table | postgres

Note the Owner column: here the tables belong to postgres, the superuser. This is precisely what a service instance must not do, as the section "How do you create a service's objects?" shows.

CommandWhat it shows
\lThe databases and their owners
\c <database>Connect to another database
\dn+The schemas, with their privileges
\dt [schema.*]The tables of a schema
\d+ <table>The detailed structure of a table
\duThe roles and their attributes
\x autoVertical display when the output is too wide
\h <command>Help on an SQL command
\?The list of all meta-commands
\qQuit psql

None of these commands ends with a semicolon, since psql handles them without sending them to the server.

Two useful administration queries

These two queries read the system views and answer two everyday questions: where the disk space goes, and who is connected. They need no extension.

SELECT datname, pg_size_pretty(pg_database_size(datname)) AS taille
FROM pg_database WHERE datistemplate = false
ORDER BY pg_database_size(datname) DESC;
datname | taille
-----------+---------
lab_admin | 7894 kB
postgres | 7654 kB
SELECT pid, usename, datname, state, LEFT(query, 60) AS query
FROM pg_stat_activity WHERE backend_type = 'client backend';
pid | usename | datname | state | query
------+----------+-----------+--------+--------------------------------------------------------------
5504 | postgres | lab_admin | active | SELECT pid, usename, datname, state, LEFT(query ...

To go beyond these two snapshots of the instance, continuous observation of instance activity and slow queries are covered in Superviser et maintenir PostgreSQL (in French).

How do you create a service's objects?

An application must never write to the database as a superuser. So you create a service role for it, and give it a schema and tables it owns. Objects follow the role that creates them: connecting as postgres to create everything makes postgres the owner, and you then have to fix it.

The appli role and the facturation database already exist (the lab provides them). The schema is created with AUTHORIZATION, which makes appli its owner, from a session opened on the facturation database:

CREATE SCHEMA appli AUTHORIZATION appli;
CREATE TABLE appli.clients (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom TEXT NOT NULL
);
INSERT INTO appli.clients (nom) VALUES ('Alice Martin'), ('Bob Dupont');

If the table was created by another account, an ALTER TABLE appli.clients OWNER TO appli; restores ownership. Finally check in the catalog, not in your memory:

SELECT tablename, tableowner FROM pg_tables WHERE schemaname = 'appli';

The tableowner column must read appli. Counting the tables of a schema is done the same way, with SELECT count(*) FROM pg_tables WHERE schemaname = 'appli';. The SQL language itself (types, constraints, transactions, DROP) is taught in the SQL course, not here.

Put it into practice

The lab replays this path on a PostgreSQL 18 instance that is running but closed: it listens only on the loopback, knows no network line in pg_hba.conf and has neither a .pgpass nor any application object. You open network access, set up a .pgpass in 0600, create the service's schema and table, then fill in a report of four values. The validation reads the state of the machine: listening addresses, pg_hba.conf lines, the .pgpass mode, objects present in the catalog, and each value of the report compared with what the server answers. A TCP connection with no password supplied serves as the final proof.

Troubleshooting

Connection errors are sorted by their origin. A FATAL message comes from the server and concerns the role or the authentication; a message prefixed psql: error: comes from the client, often because it found nobody at the end of the channel. The table links each symptom to the door that blocks, then to the file to fix.

SymptomDoorLikely causeFix
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory, followed by Is the server running locally and accepting connections on that socket?ServiceThe service is not startedpg_isready, then sudo systemctl status postgresql@18-main
connection to server at "192.168.122.60", port 5432 failed: Connection refused, followed by Is the server running on that host and accepting TCP/IP connections?listen_addressesThe server does not listen on this address (or port)Fix postgresql.conf, restart, check with ss -ltn sport = :5432
FATAL: no pg_hba.conf entry for host "192.168.122.60", user "appli", database "facturation", no encryption (or pg_hba.conf rejects connection for host ... if a reject line matches)pg_hba.confNo host line matches the address, the role and the databaseAdd the line, reload, check pg_hba_file_rules
FATAL: Peer authentication failed for user "postgres"MethodSocket connection where the requested role (here -U postgres) differs from the system usersudo -u postgres psql, or switch to TCP
FATAL: role "bob" does not existRoleThe role does not exist\du, then CREATE ROLE bob LOGIN PASSWORD '<change-me>';
Password for user ...: then fe_sendauth: no password supplied with -w.pgpassTwo causes for the same symptom: a mode other than 0600 (preceded by WARNING: password file "..." has group or world access on stderr), or a host in the file that differs from -h (no warning)Rerun without 2>/dev/null, chmod 600 and check with stat, then align the file's host with -h
The output is truncated or pagedDisplayWidth or pager\x auto, \pset pager off

To tell these causes apart, test in the order of the doors: ss -ltn first, then pg_hba_file_rules, finally psql -w, which fails rather than asking for a password when the .pgpass is ignored. Faced with no password supplied, check the permissions then the host: the two failures look alike.

Key points

  • A running instance is not reachable: three doors to go through, listen_addresses, pg_hba.conf and the password on the client side.
  • listen_addresses requires a restart; pg_hba.conf is reloaded.
  • On a network line, scram-sha-256 is the only acceptable method here: trust lets anyone in without asking, password sends the secret in clear.
  • A .pgpass with a mode other than 0600 is ignored, with a warning on stderr that 2>/dev/null hides; a host that does not match gives the same failure with no warning at all. Create it with install -m 0600 and prove it with psql -w.
  • A service's objects belong to the service role, not to postgres: CREATE SCHEMA ... AUTHORIZATION, ALTER TABLE ... OWNER TO.
  • pg_settings and the catalog tell you what the server does, not what you think you wrote.
  • \copy, the -v variables, \gset and transactions belong to the SQL course.

Knowledge check

Eight questions on the three doors of a network connection, the trap of an overly permissive .pgpass, the difference between restart and reload, and the ownership of a service's objects.

Knowledge check

Check what you have learned with this interactive quiz

8 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn