
Every few months, someone on a Linux forum asks why their fresh Fedora install won’t let them connect to PostgreSQL after a “successful” installation. The answer is almost always the same: Fedora’s PostgreSQL packaging behaves differently from Debian-based distros, and skipping the initialization step or misreading the authentication config leads straight into a wall of “password authentication failed” errors. If you’ve landed here after hitting that wall, you’re in good company.
Fedora 44 ships with a reasonably current PostgreSQL package in its default repos, but that’s not always what you want. Production environments often need a specific major version—PostgreSQL 16, 17, or the newly released 18—which means pulling from the official PGDG (PostgreSQL Global Development Group) repository instead of relying on whatever Fedora’s maintainers decided to package this cycle. This distinction trips up a lot of people who assume dnf install postgresql-server gives them full control over versioning. It doesn’t, not really.
This guide walks through both paths: the quick native Fedora route for development boxes and quick testing, and the PGDG repository method that most production admins actually use. Along the way, we’ll cover the initialization quirks specific to RPM-based systems, authentication configuration that actually works, firewall rules, SELinux considerations (because Fedora enforces SELinux by default and PostgreSQL will absolutely trip over it if you’re not careful), and the tuning knobs that matter once real traffic hits the server. If you’ve managed PostgreSQL on Ubuntu before, expect a few surprises—Fedora does things its own way, and pretending otherwise costs time you don’t have during an incident.
Why PostgreSQL on Fedora 44 Requires Extra Care
Fedora is a fast-moving distribution. Package versions churn every six months, and the default PostgreSQL version bundled in the repos shifts accordingly. That’s fine for a laptop running experiments, but it’s a liability for a production database server where you need predictable upgrade paths and long-term support commitments matching what PGDG offers.
There’s also the initialization behavior difference. On Debian and Ubuntu, apt install postgresql automatically creates and starts a database cluster for you. On Fedora, RHEL, and other RPM-based systems, installing the package does not initialize the data directory—you have to run postgresql-setup --initdb explicitly. Miss that step, and systemctl start postgresql will fail silently or throw a cryptic error about a missing data directory. This single difference accounts for a disproportionate share of “PostgreSQL won’t start on Fedora” support threads.
Prerequisites Before You Start
Before touching any package manager commands, confirm a few basics. None of this is glamorous, but skipping it is exactly how you end up debugging the wrong problem for forty minutes.
- A Fedora 44 system with root or sudo access, updated via
sudo dnf upgrade --refresh - At least 1 GB of free RAM for a small instance; production workloads need considerably more depending on
shared_buffersand connection count - Sufficient disk space on a partition that won’t hit inode or space limits—database bloat is real and happens faster than people expect
- Basic familiarity with SELinux contexts, since Fedora enforces SELinux by default and PostgreSQL ships with policies that assume standard data directory locations
- Firewalld running (Fedora’s default), which you’ll need to open port 5432 for remote access later
Method 1: Installing PostgreSQL from Fedora’s Default Repositories
This is the fastest route and works well for local development, testing environments, or situations where the version Fedora ships happens to match what you need.
Step 1: Update the System
sudo dnf upgrade --refresh
Running this first avoids dependency conflicts during installation—an old kernel or outdated glibc can occasionally cause weird build-time issues with database extensions later.
Step 2: Install PostgreSQL Server and Client Packages
sudo dnf install postgresql postgresql-server postgresql-contrib
The postgresql-contrib package deserves a mention because people frequently skip it, then wonder why extensions like pg_stat_statements or uuid-ossp aren’t available. It’s a small package, but it saves a second round trip later.
Step 3: Initialize the Database Cluster
sudo postgresql-setup --initdb
For Fedora 43 and later derived distributions, this command creates the data directory (typically /var/lib/pgsql/data) and sets up the initial cluster configuration. This is the step Debian users forget exists, because their distro does it automatically. Here, it’s manual, and it matters.
Step 4: Enable and Start the Service
sudo systemctl enable postgresql.service
sudo systemctl start postgresql.service
Verify it’s actually running rather than just assuming success:
sudo systemctl status postgresql.service
Look for active (running) in the output. A failed status almost always traces back to a missed initdb step or a permissions problem on the data directory.
Method 2: Installing a Specific PostgreSQL Version via PGDG Repository
This is the method most production teams actually use, because it decouples your database version from Fedora’s release cycle and gives you access to the latest point releases directly from the PostgreSQL project.
Step 1: Add the PGDG Repository
sudo dnf install https://download.postgresql.org/pub/repos/yum/reporpms/F-44-x86_64/pgdg-fedora-repo-latest.noarch.rpm
This repo RPM registers multiple version-specific repos (pgdg14 through pgdg18) simultaneously, which is convenient but also means dnf might try to resolve dependencies against a version you didn’t intend to install.
Step 2: Disable Repos for Versions You Don’t Need
sudo dnf config-manager setopt pgdg14.enabled=0 pgdg15.enabled=0 pgdg16.enabled=0 pgdg17.enabled=0
Adjust this list based on which version you’re targeting—if you want PostgreSQL 17 instead of 18, disable pgdg18 rather than pgdg17. Confirm the change:
sudo dnf repolist --enabled | grep pgdg
You should see only the target version’s repo and pgdg-common listed.
Step 3: Install PostgreSQL 18 (or Your Chosen Version)
sudo dnf install postgresql18-server
Note the package naming convention—PGDG packages include the version number directly, unlike Fedora’s generic postgresql-server package. This matters when scripting deployments, because the binary paths and service names change per version too.
Step 4: Initialize the Cluster
sudo /usr/pgsql-18/bin/postgresql-18-setup initdb
Every PGDG-installed version gets its own binary directory under /usr/pgsql-<version>/, which is intentional—it lets you run multiple PostgreSQL versions side by side during migrations without conflict.
Step 5: Enable and Start the Service
sudo systemctl enable postgresql-18 --now
The --now flag combines enabling and starting in one command, a small convenience that saves a keystroke but also reduces the chance of forgetting to actually start the thing after enabling it.
Setting the Postgres Superuser Password
Right after installation, the postgres OS-level user can access the database without a password via peer authentication, but the database-level postgres role typically has no password set. Leaving this unset is a rookie mistake that occasionally survives into production—don’t let it be yours.
sudo -u postgres psql
Inside the psql prompt:
\password postgres
Enter a strong password when prompted. If you need a dedicated superuser role tied to your own OS username instead of relying on the shared postgres account, create one explicitly:
CREATE USER yourusername WITH SUPERUSER PASSWORD 'a-strong-password-here';
Using named accounts per admin, rather than sharing the postgres login, makes audit logs actually mean something later—when three people share credentials, “who ran that DROP TABLE” becomes an unanswerable question.
Configuring pg_hba.conf for Remote and Local Access
This file controls who can connect, from where, and using which authentication method—and it’s the single most common source of “connection refused” or “no pg_hba.conf entry” errors.
Locate the file depending on your install method:
- Fedora native package:
/var/lib/pgsql/data/pg_hba.conf - PGDG package:
/var/lib/pgsql/18/data/pg_hba.conf
Open it with a text editor:
sudo nano /var/lib/pgsql/18/data/pg_hba.conf
By default, local connections use peer authentication (checking OS username against DB username) and remote connections are often not permitted at all. For a typical application server setup, add or modify:
# TYPE DATABASE USER ADDRESS METHOD
host all all 127.0.0.1/32 scram-sha-256
host all all 10.0.0.0/24 scram-sha-256
Avoid trust authentication in anything resembling production—it allows connections without a password entirely, which is fine for an isolated sandbox VM and a liability everywhere else. Use scram-sha-256 rather than the older md5 method; it’s the current recommended hashing scheme and md5 is considered legacy at this point.
After editing, also check postgresql.conf for the listen_addresses setting:
listen_addresses = 'localhost'
Change this to a specific IP or * only if remote access is genuinely required, and pair it with firewall restrictions rather than opening the port to the world.
Opening the Firewall for PostgreSQL
Fedora runs firewalld by default, and PostgreSQL’s port 5432 is blocked out of the box. If you need remote connectivity:
sudo firewall-cmd --permanent --add-service=postgresql
sudo firewall-cmd --reload
If the predefined service doesn’t match your custom port, open it directly:
sudo firewall-cmd --permanent --add-port=5432/tcp
sudo firewall-cmd --reload
Scope this to trusted source zones wherever possible. Exposing 5432 directly to the internet, even with strong passwords, invites automated scanning and brute-force attempts within hours—this isn’t theoretical, it happens on real servers constantly.
SELinux Considerations
SELinux enforcement is where Fedora setups quietly diverge from CentOS/AlmaLinux defaults in subtle ways, especially when data directories move off the standard path. If you relocate the PostgreSQL data directory to a custom mount point (common when separating data onto faster storage), SELinux will block the postgres process from accessing it unless you update the context:
sudo semanage fcontext -a -t postgresql_db_t "/custom/pgdata(/.*)?"
sudo restorecon -Rv /custom/pgdata
Skipping this step produces a service that starts, logs nothing obviously wrong, and simply refuses to write to disk—an infuriating failure mode if you don’t know to check audit.log first.
Restarting the Service and Verifying Connectivity
After configuration changes, restart rather than reload for pg_hba.conf and postgresql.conf edits that touch authentication logic:
sudo systemctl restart postgresql-18
Test locally:
psql -U postgres -h 127.0.0.1 -W
And from a remote host, once firewall rules are in place:
psql -U yourapp -h your-server-ip -d yourdatabase -W
Creating a Database and Application User
Running production workloads as the postgres superuser is a bad habit that eventually causes damage—an application bug that would normally just fail gracefully can instead drop a table it should never have had permission to touch.
CREATE USER appuser WITH PASSWORD 'strong-unique-password';
CREATE DATABASE appdb OWNER appuser;
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;
Then, as a matter of habit, revoke unnecessary public schema access—this closes off a class of privilege escalation issues that catch teams off guard:
REVOKE ALL ON SCHEMA public FROM PUBLIC;
Troubleshooting Common Installation Issues
“FATAL: database files are incompatible with server”
This happens when a data directory initialized by one major version gets loaded by a binary from a different version—common after upgrades where the old cluster wasn’t properly migrated with pg_upgrade. Check version alignment between the binary and the data directory before assuming corruption.
Service starts then immediately fails
Check logs with journalctl -u postgresql-18 -n 50 --no-pager. Nine times out of ten it’s either a permissions issue on the data directory (should be owned by the postgres user, mode 0700) or a port conflict with another running instance.
“Peer authentication failed for user”
This occurs when connecting locally without matching your OS username to the database role, under peer auth mode. Either connect as the matching OS user, switch that pg_hba.conf line to md5/scram-sha-256, or use sudo -u postgres psql explicitly.
Connection refused from remote host
Almost always one of three things: listen_addresses still set to localhost, firewalld blocking the port, or a missing entry in pg_hba.conf for the connecting subnet. Check all three in that order—it’s faster than guessing.
“No pg_hba.conf entry for host”
The connecting IP or database name doesn’t match any rule in pg_hba.conf. Add a specific entry rather than a broad wildcard; broad rules are convenient until an audit asks why anyone on the subnet can connect to a finance database.
Performance Tuning for Production Workloads
Default PostgreSQL settings are conservative by design—they’re meant to run on modest hardware without crashing, not to extract maximum throughput from a dedicated database server. Tuning starts with a handful of parameters in postgresql.conf.
shared_buffers: set to roughly 25% of total system RAM on a dedicated DB server; this is PostgreSQL’s own page cache and undersizing it forces excessive disk readseffective_cache_size: set to about 50-75% of total RAM, telling the planner how much OS-level caching it can expect, which changes query plan choices significantlywork_mem: increase cautiously for workloads with heavy sorts and hash joins, but remember this is allocated per operation, per connection—too generous a value under high concurrency exhausts RAM fastmaintenance_work_mem: bump this higher than work_mem for faster VACUUM and index builds, since it’s used less frequently and by fewer concurrent operationsmax_connections: resist the urge to set this arbitrarily high; pair it with connection pooling via PgBouncer instead of scaling raw connection limits, since each connection carries real memory overheadcheckpoint_completion_target: set closer to 0.9 to spread checkpoint I/O over a longer window, reducing the periodic write spikes that cause latency stalls during traffic peaks
On disk I/O, if the server sits on NVMe or SSD storage, set random_page_cost closer to 1.1 rather than the spinning-disk-era default of 4.0—this alone has changed query plans dramatically on servers migrated from HDD-based storage over the years. For servers under sustained write load, moving the WAL (write-ahead log) to a separate physical disk from the main data files reduces I/O contention meaningfully, especially during traffic spikes when checkpoint and WAL writes compete for the same spindle bandwidth.
Security Hardening Beyond the Basics
Getting PostgreSQL running is the easy part. Keeping it secure over years of operation is where the real discipline shows.
- Enable SSL/TLS for connections in transit by setting
ssl = onin postgresql.conf and requiringsslmode=requireon client connections, especially for anything crossing a network boundary. - Store passwords using
scram-sha-256, notmd5—the latter is considered legacy and weaker against offline attacks. - Enable logging for connections, disconnections, and DDL statements (
log_connections,log_disconnections,log_statement = 'ddl') so incident investigations have something to work with. - Consider
pgAuditfor fine-grained auditing on systems handling sensitive or regulated data. - Automate regular
pg_dumpbackups with encrypted storage, and actually test restores periodically—an untested backup is a hypothesis, not a safety net. - Subscribe to the
pgsql-announcemailing list to stay ahead of CVE disclosures affecting your installed version.