How to Install and Start PostgreSQL on Linux

How to Install and Start PostgreSQL on Linux

Reading time1 min
#postgresql#database#linux#ubuntu#rhel

How to Install and Start PostgreSQL on Linux

PostgreSQL packaging differs more between distro families than most software. On Debian and Ubuntu the package creates and starts a cluster for you. On the RHEL family you initialize the data directory yourself before the service will start. The service names and config locations differ too. This post covers both, plus the first user and database, remote access, and what to check when the server will not start.

Distro packages or PGDG

Every distro ships one PostgreSQL major version and keeps it for the life of the release: Ubuntu 22.04 has 14, 24.04 has 16, 26.04 has 18. AlmaLinux, Rocky and RHEL 9 ship 13 by default and offer 15, 16 and (since 9.8) 18 as module streams; EL10 ships 16, with 18 available as postgresql18-server.

The PostgreSQL project's own repositories (PGDG) carry every supported major version for each distro release. Use them when you want a newer version than the distro has, or several versions side by side. As of October 2026, PostgreSQL 18 is the latest stable release (19 is due soon), and 14 reaches end of life in November 2026, so on Ubuntu 22.04 PGDG is the better choice for a new install.

Debian and Ubuntu

From the distro:

sudo apt update
sudo apt install postgresql

From PGDG, using the helper script that ships in postgresql-common (it adds the repository and its signing key):

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install postgresql-18

Either way a cluster called main is created and started. Check it:

pg_lsclusters
sudo -u postgres psql -c 'SELECT version();'

Config lives in /etc/postgresql/<version>/main/, data in /var/lib/postgresql/<version>/main/, logs in /var/log/postgresql/.

RHEL, AlmaLinux, Rocky Linux

From the distro (on EL9, pick a newer stream first with sudo dnf module enable postgresql:16):

sudo dnf install postgresql-server
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql

From PGDG:

sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %rhel)-$(uname -m)/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql      # EL8 and EL9 only
sudo dnf install -y postgresql18-server
sudo /usr/pgsql-18/bin/postgresql-18-setup initdb
sudo systemctl enable --now postgresql-18

The module disable step stops the distro's PostgreSQL packages from shadowing the PGDG ones. Data and config live in /var/lib/pgsql/data/ for the distro package and /var/lib/pgsql/18/data/ for PGDG. The service is postgresql or postgresql-18 accordingly. Skipping initdb is the most common reason the service fails on first start.

Start, stop, reload

sudo systemctl start postgresql       # postgresql-18 for PGDG on RHEL
sudo systemctl stop postgresql
sudo systemctl restart postgresql
sudo systemctl reload postgresql
sudo systemctl enable postgresql      # start at boot
pg_isready                            # accepting connections?

On Debian and Ubuntu, postgresql.service is an umbrella unit. Each cluster has its own unit, postgresql@16-main, and pg_ctlcluster controls one cluster directly:

sudo systemctl status postgresql@16-main
sudo pg_ctlcluster 16 main restart

Reload is enough for pg_hba.conf changes and most settings, and it does not drop connections. A few settings, such as listen_addresses, port and shared_buffers, need a full restart. To see which ones:

SELECT name FROM pg_settings WHERE context = 'postmaster';

First user and database

PostgreSQL creates an OS user and a database superuser, both called postgres. Local connections use peer authentication, so you connect as that OS user:

sudo -u postgres createuser --pwprompt myapp
sudo -u postgres createdb --owner=myapp myapp
psql -h localhost -U myapp myapp

-h localhost forces a TCP connection, which uses password authentication instead of peer. On the RHEL family the generated pg_hba.conf may use ident for local TCP connections; if login fails with Ident authentication failed, change those host lines to scram-sha-256 and reload.

Remote access

Find the config files first, since their location depends on the packaging:

sudo -u postgres psql -c 'SHOW config_file;' -c 'SHOW hba_file;'

In postgresql.conf, listen on the private address (or '*' for all):

listen_addresses = 'localhost,10.0.0.5'

In pg_hba.conf, allow one database and user from the application network, with password hashing:

# TYPE  DATABASE  USER   ADDRESS       METHOD
host    myapp     myapp  10.0.0.0/24   scram-sha-256

Restart (because listen_addresses changed) and open the port only to that network:

sudo systemctl restart postgresql
sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp
# RHEL: sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" service name="postgresql" accept'
#       sudo firewall-cmd --reload

When it will not start

Look at the logs first:

sudo journalctl -u postgresql -n 50            # or postgresql@16-main / postgresql-18
sudo tail -n 50 /var/log/postgresql/postgresql-16-main.log   # Debian/Ubuntu
sudo ls /var/lib/pgsql/18/data/log/            # RHEL, logging_collector writes here

The usual causes:

  • Data directory not initialized (RHEL). Run the initdb step above.
  • Port 5432 already in use. Another cluster or version is running. On Debian a second cluster quietly gets 5433; pg_lsclusters shows ports. sudo ss -ltnp 'sport = :5432' shows the process.
  • data directory ... has invalid permissions. The data directory must be owned by postgres with mode 0700 (or 0750). Fix with chown -R postgres:postgres and chmod 700.
  • Data directory moved, SELinux blocks it (RHEL). Label the new path and point the unit at it:
    sudo semanage fcontext -a -t postgresql_db_t "/data/pgsql(/.*)?"
    sudo restorecon -R /data/pgsql
    sudo systemctl edit postgresql-18     # add: [Service] Environment=PGDATA=/data/pgsql/18/data
    
  • Peer authentication failed for user "postgres". Not a startup problem. You ran psql -U postgres as a different OS user. Use sudo -u postgres psql.

For debugging outside systemd, pg_ctl runs the server by hand. Use the same data directory and OS user as the service, and stop the service first:

sudo -u postgres /usr/pgsql-18/bin/pg_ctl -D /var/lib/pgsql/18/data -l /tmp/pg.log start

On Debian and Ubuntu use pg_ctlcluster instead, because the config lives outside the data directory.

A first tuning step

The defaults are sized for a tiny machine. The PostgreSQL docs suggest about 25% of RAM for shared_buffers on a dedicated database server as a starting point. Change it in postgresql.conf and restart. Leave the rest at defaults until you have a real workload to measure.

Quick reference

sudo apt install postgresql                                 # Debian/Ubuntu, cluster auto-created
sudo dnf install postgresql-server && sudo postgresql-setup --initdb   # RHEL family
sudo systemctl enable --now postgresql                      # postgresql-18 for PGDG on RHEL
pg_lsclusters                                               # Debian/Ubuntu clusters and ports
pg_isready
sudo -u postgres psql
sudo -u postgres createuser --pwprompt myapp && sudo -u postgres createdb --owner=myapp myapp
sudo systemctl reload postgresql                            # after pg_hba.conf changes