How to Secure a PostgreSQL Installation on Fedora

Install PostgreSQL on Fedora, enable the firewall, and configure the database to listen only on localhost for local security.

How to Secure a PostgreSQL Installation on Fedora

You installed PostgreSQL on your Fedora workstation to back up your home lab data. You ran the quick install guide, the service started, and now you are staring at the terminal wondering if your database is wide open to the internet. Or maybe you are setting up a server and you know you need to lock things down before you push the connection string to your application. The default Fedora package does a lot of the heavy lifting, but a few manual steps keep prying eyes out and ensure the service behaves exactly how you expect.

What controls access to your database

PostgreSQL security on Fedora relies on three layers working together. The package manager drops configuration files into /etc/postgresql/. The firewall daemon, firewalld, manages the kernel's netfilter rules. The database process itself reads its config to decide which network interfaces it binds to.

If you skip the firewall, the port is open to anyone on your network. If you skip the config change, the database might listen on all interfaces, including public ones. If you skip enabling the service, the database vanishes after a reboot. The goal is to make the database accessible only to applications running on the same machine, or only to trusted internal IPs if you have a multi-server setup.

Config files live in /etc/postgresql/. The package ships defaults in /usr/lib/pgsql/. Always edit the files in /etc/. If you modify files in /usr/lib/, the next dnf upgrade will overwrite your changes and you will lose your work.

Install and initialize the database

Start by installing the packages and creating the initial database cluster. The postgresql-setup command handles the directory structure and permissions so you do not have to create the postgres user manually.

sudo dnf install -y postgresql postgresql-server
# WHY: Installs the client tools and the server daemon.
# The -y flag auto-confirms the transaction to keep the script clean.

sudo postgresql-setup --initdb --unit postgresql
# WHY: Creates the initial database cluster in /var/lib/pgsql/data.
# The --unit flag ensures the config points to the correct systemd unit name.

sudo systemctl enable --now postgresql
# WHY: Starts the service immediately and registers it to start on boot.
# Without enable, the database stops working after you reboot the system.

The --unit flag in postgresql-setup is important. It writes the correct service name into the configuration so that systemctl and the database daemon agree on how to communicate. If you omit it, you might end up with a cluster that starts but systemd cannot manage correctly.

Run dnf upgrade --refresh weekly to keep the database engine patched. This command refreshes the metadata and applies security updates. Do not confuse this with dnf system-upgrade, which is reserved for crossing major Fedora releases like 40 to 42.

Lock down the network with the firewall

Fedora enables firewalld by default, but the PostgreSQL service is not allowed through until you add it. The firewall rules are split into a runtime configuration that applies immediately and a permanent configuration that survives reboots. You must modify the permanent zone and then reload to apply changes.

sudo firewall-cmd --permanent --add-service=postgresql
# WHY: Adds the PostgreSQL service definition to the permanent firewall rules.
# The permanent zone survives reboots and firewall reloads.

sudo firewall-cmd --reload
# WHY: Applies the permanent rules to the running firewall configuration.
# Changes to the permanent zone do not take effect until you reload.

Always run firewall-cmd --reload after modifying permanent rules. The runtime configuration and the persistent configuration are separate. If you skip the reload, your changes sit in a file on disk but the kernel keeps blocking or allowing traffic based on the old rules. This divergence causes confusion when troubleshooting connectivity issues later.

Configure the database to listen on localhost

By default, PostgreSQL might listen on all interfaces depending on the version and init script. For a secure local setup, restrict the listener to the loopback interface. This ensures the database only accepts connections from processes running on the same machine.

Use ALTER SYSTEM to update the configuration. This command writes to postgresql.auto.conf, which overrides the main postgresql.conf file. This approach is safer than editing files manually because it preserves your settings across package updates.

sudo -u postgres psql -c "ALTER SYSTEM SET listen_addresses = 'localhost';"
# WHY: Updates the postgresql.auto.conf file to bind only to the loopback interface.
# Running as the postgres user ensures correct file permissions without sudo hacks.

sudo systemctl reload postgresql
# WHY: Tells the running daemon to re-read configuration files without dropping connections.
# A full restart would disconnect all active clients, which is unnecessary for config changes.

Reload, do not restart. A reload applies configuration changes while keeping active connections alive. A restart kills all sessions and starts the process from scratch, which is disruptive if other applications are using the database.

Snapshot the config before you change listen_addresses. If you lock yourself out of the database by binding to an interface you cannot reach, you can restore the file from a backup and recover access.

Verify the configuration

Check that the service is running, the port is bound to the correct interface, and the firewall is active. The systemctl status command shows the state and recent log lines in one view. Always check status before restarting or reloading a unit.

sudo systemctl status postgresql
# WHY: Shows the current state, recent log lines, and the main process ID.
# Look for "active (running)" in green text. If it is failed, check the log lines below.

sudo ss -tlnp | grep 5432
# WHY: Lists listening TCP sockets and filters for the default PostgreSQL port.
# You should see 127.0.0.1:5432 or [::1]:5432. If you see 0.0.0.0:5432, the config did not apply.

Check the socket binding first. If the port is open on 0.0.0.0, your config change did not stick. Verify that listen_addresses is set correctly in postgresql.auto.conf and that you reloaded the service.

Common pitfalls and error messages

Errors usually fall into three categories: the service is not running, the firewall is blocking traffic, or the authentication method is wrong.

If you try to connect and see psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory, the service is not running or the socket directory is missing. Run sudo systemctl status postgresql to check the state. If the service failed, check the journal for the root cause.

If you see psql: error: could not connect to server: Connection timed out, the firewall is likely dropping the packet. Verify the service is allowed with sudo firewall-cmd --list-services. If PostgreSQL is missing from the list, add it and reload.

If you set listen_addresses in postgresql.conf directly and wonder why it does not work, check postgresql.auto.conf. The auto file takes precedence over the main config file. Settings in auto.conf override conflicting settings in postgresql.conf. Use ALTER SYSTEM to manage settings consistently.

SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. Fedora's SELinux policy is strict by design. Disabling SELinux opens more holes than it closes. If you see a denial related to PostgreSQL, use semanage or audit2allow to fix the specific context rather than turning the protection off.

Read the error message. "Connection refused" means the service is down. "Connection timed out" means the firewall is blocking you. "Authentication failed" means pg_hba.conf is rejecting the credentials.

When to use each configuration approach

Use listen_addresses = 'localhost' when your application runs on the same machine as the database. Use listen_addresses = '*' when you have remote clients and you have configured pg_hba.conf to restrict access by IP and authentication method. Use firewall-cmd --add-rich-rule when you need to allow access from a specific subnet rather than opening the port to the entire network. Use ALTER SYSTEM for configuration changes that must survive package upgrades and manual edits. Use direct file edits in /etc/postgresql/ only when you are debugging a complex configuration issue and need to see the raw syntax.

Where to go next