Void Linux is one of those distributions that rewards you for understanding how your system works. No systemd, no hand-holding, just runit and XBPS doing exactly what you tell them. PostgreSQL fits right into that philosophy: a database engine that does one thing extremely well and stays out of your way.
This guide walks through installing PostgreSQL 16 on Void Linux, initializing the database cluster, creating a runit service from scratch (Void doesn’t ship one), and locking down authentication. If you’re coming from Debian or RHEL, the biggest difference is the service management piece, which we’ll cover in detail. For PostgreSQL on RHEL-based systems, see PostgreSQL on Rocky Linux 10.
Tested March 2026 | Void Linux (glibc), PostgreSQL 16.10
Prerequisites
- Void Linux (glibc variant, x86_64) with root or sudo access
- XBPS package manager up to date
- Tested on: Void Linux rolling (glibc), PostgreSQL 16.10
1. Install PostgreSQL
Void’s XBPS repository ships PostgreSQL 16.x. Install the server and client packages:
sudo xbps-install -Sy postgresql16 postgresql16-client
Confirm the installed version:
psql --version
You should see the version string confirming 16.x:
psql (PostgreSQL) 16.10
XBPS creates a postgres system user automatically during installation. Verify it exists:
id postgres
The output confirms the user and group:
uid=997(postgres) gid=997(postgres) groups=997(postgres)
2. Initialize the Database Cluster
PostgreSQL needs an initialized data directory before it can start. Create the directory and set ownership to the postgres user:
sudo mkdir -p /var/lib/postgresql/data
sudo chown postgres:postgres /var/lib/postgresql/data
Now initialize the cluster. This must run as the postgres user:
sudo -u postgres initdb -D /var/lib/postgresql/data
The initialization output shows the cluster being created with default settings:
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".
Data page checksums are disabled.
fixing permissions on existing directory /var/lib/postgresql/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default "timezone" ... UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option -A,
or --auth-local and --auth-host, the next time you run initdb.
Success. You can now start the database server using:
pg_ctl -D /var/lib/postgresql/data -l logfile start
Notice the warning about trust authentication. We’ll tighten that up after getting the service running.
3. Create a Runit Service
Void Linux uses runit for service management, and PostgreSQL doesn’t ship with a runit service file by default. You need to create one manually. This catches most people off guard when coming from distributions where systemctl enable postgresql just works.
Create the service directory structure:
sudo mkdir -p /etc/sv/postgresql/log
Create the main run script that starts PostgreSQL as the postgres user:
sudo vi /etc/sv/postgresql/run
Add the following content:
#!/bin/sh
exec chpst -u postgres postgres -D /var/lib/postgresql/data 2>&1
Make it executable:
sudo chmod +x /etc/sv/postgresql/run
Now create the log service. Runit expects a log/run script to capture output via svlogd:
sudo vi /etc/sv/postgresql/log/run
Add the logging script:
#!/bin/sh
exec svlogd -tt /var/log/postgresql
Make it executable and create the log directory:
sudo chmod +x /etc/sv/postgresql/log/run
sudo mkdir -p /var/log/postgresql
Enable the service by creating a symlink to /var/service/. Runit picks it up automatically within a few seconds:
sudo ln -s /etc/sv/postgresql /var/service/
Check the service status:
sudo sv status postgresql
A healthy service looks like this:
run: postgresql: (pid 1423) 5s; run: log: (pid 1420) 5s
If the status shows down, check the log at /var/log/postgresql/current for errors. Common causes include wrong permissions on the data directory or a missing postgres user.
4. Secure the Installation
The default initdb configuration uses trust authentication for local connections, which means any local user can connect as any database user without a password. Fix this by setting a password for the postgres superuser and switching to password-based authentication.
Connect to PostgreSQL and set the superuser password:
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'StrongPassword123!';"
The response confirms the change:
ALTER ROLE
Now edit pg_hba.conf to enforce password authentication. Open the file:
sudo vi /var/lib/postgresql/data/pg_hba.conf
Find the lines near the bottom that control local connections and change trust to scram-sha-256:
# "local" is for Unix domain socket connections only
local all all scram-sha-256
# IPv4 local connections:
host all all 127.0.0.1/32 scram-sha-256
# IPv6 local connections:
host all all ::1/128 scram-sha-256
Reload PostgreSQL to apply the changes without dropping existing connections:
sudo -u postgres pg_ctl reload -D /var/lib/postgresql/data
The reload confirmation:
server signaled
From now on, every connection requires a password. Test it:
psql -U postgres -h 127.0.0.1 -c "SELECT version();"
Enter the password you set earlier. The query output shows your running version:
version
---------------------------------------------------------------------------------------------------------
PostgreSQL 16.10 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 13.2.0, 64-bit
(1 row)
5. Create a Database and User
Production applications should never connect as the postgres superuser. Create a dedicated user and database for your application.
Connect as the superuser first:
psql -U postgres -h 127.0.0.1
Create a new user, database, and grant privileges. This is an interactive psql session:
CREATE USER appuser WITH PASSWORD 'AppUserPass456!';
CREATE DATABASE appdb OWNER appuser;
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;
\q
Verify the new user can connect to the database:
psql -U appuser -h 127.0.0.1 -d appdb -c "\conninfo"
The connection info confirms the user and database:
You are connected to database "appdb" as user "appuser" on host "127.0.0.1" at port "5432".
6. Enable Remote Connections (Optional)
By default, PostgreSQL 16 listens only on localhost. If other machines on your network need to connect, you’ll need to change the listen address and add a firewall rule.
Open the main configuration file:
sudo vi /var/lib/postgresql/data/postgresql.conf
Find the listen_addresses line and change it to listen on all interfaces:
listen_addresses = '*'
Next, add a line to pg_hba.conf to allow connections from your network. Open the file:
sudo vi /var/lib/postgresql/data/pg_hba.conf
Add this line at the end, adjusting the subnet to match your network:
# Allow connections from local network
host all all 10.0.1.0/24 scram-sha-256
Restart PostgreSQL to apply the listen address change (a reload isn’t enough for listen_addresses):
sudo sv restart postgresql
Void Linux uses nftables for firewalling. Allow TCP port 5432:
sudo nft add rule inet filter input tcp dport 5432 accept
To make the firewall rule persistent across reboots, save the current ruleset:
sudo nft list ruleset | sudo tee /etc/nftables.conf
Verify PostgreSQL is listening on all interfaces:
ss -tlnp | grep 5432
The output should show 0.0.0.0:5432 instead of 127.0.0.1:5432:
LISTEN 0 244 0.0.0.0:5432 0.0.0.0:* users:(("postgres",pid=1423,fd=6))
7. Basic psql Commands
Quick reference for the most common psql commands you’ll use daily. Connect with psql -U postgres -h 127.0.0.1 and run these from the psql prompt:
| Command | Description |
|---|---|
\l | List all databases |
\dt | List tables in the current database |
\c dbname | Connect to a different database |
\d tablename | Describe a table (columns, types, indexes) |
\du | List all roles/users |
SELECT version(); | Show the PostgreSQL server version |
\timing | Toggle query execution time display |
\x | Toggle expanded (vertical) output |
\q | Exit psql |
PostgreSQL on Void vs Other Distros
If you manage PostgreSQL across multiple distributions, these are the key differences to keep in mind:
| Item | Void Linux | Debian/Ubuntu | RHEL/Rocky 10 |
|---|---|---|---|
| Package name | postgresql16 | postgresql-16 | postgresql16-server |
| Data directory | /var/lib/postgresql/data | /var/lib/postgresql/16/main | /var/lib/pgsql/16/data |
| Config path | /var/lib/postgresql/data/ | /etc/postgresql/16/main/ | /var/lib/pgsql/16/data/ |
| Service manager | runit (manual setup) | systemd | systemd |
| Start command | sv up postgresql | systemctl start postgresql | systemctl start postgresql-16 |
| Auto-init on install | No | Yes | No (postgresql-setup --initdb) |
| Firewall | nftables | ufw | firewalld |
| SELinux/AppArmor | Neither | AppArmor (usually permissive) | SELinux enforcing |
The biggest difference on Void is the manual service setup. On Debian, the package creates the cluster and starts the service automatically. On Void, you handle both yourself, which gives you full control over the data directory location and startup flags.
Tuning for Production
PostgreSQL’s default configuration is deliberately conservative. On a dedicated database server, you’ll want to adjust these settings in postgresql.conf. The values below assume a server with 4 GB of RAM.
sudo -u postgres vi /var/lib/postgresql/data/postgresql.conf
Key parameters to change:
# Memory (adjust proportionally to your RAM)
shared_buffers = 1GB # 25% of total RAM
effective_cache_size = 3GB # 75% of total RAM
work_mem = 16MB # per-operation sort memory
maintenance_work_mem = 256MB # for VACUUM, CREATE INDEX
# WAL and checkpoints
wal_buffers = 64MB
checkpoint_completion_target = 0.9
max_wal_size = 2GB
# Connections
max_connections = 100 # lower this if using pgbouncer
After editing, reload the configuration without restarting:
sudo sv reload postgresql
Some parameters (like shared_buffers and max_connections) require a full restart to take effect. Check the PostgreSQL 16 documentation for which settings need a restart versus a reload. For a web application backend, pairing this with a LAMP or Nginx stack on the same Void Linux host works well for small to medium deployments.