
Understanding PostgreSQL Authentication and Role Hierarchy
PostgreSQL user management and access control operates on a role-based system. Users and groups are both represented as roles.
Every connection to your database requires authentication through one of these roles. This makes proper configuration essential for security.
Unlike traditional user/group models, PostgreSQL treats everything as roles. A role can be a user (with LOGIN privilege), a group (without LOGIN), or both.
This approach simplifies permission management while providing granular control over database access.
Your HostMyCode VPS comes with PostgreSQL pre-configured for basic operation. Production environments need custom role structures.
Essential PostgreSQL Role Creation and Configuration
Start by connecting to your PostgreSQL instance as the superuser:
sudo -u postgres psql
Create application-specific roles with targeted permissions:
-- Create a read-only role for reporting
CREATE ROLE reports_reader WITH LOGIN PASSWORD 'secure_password_2026';
-- Create an application role with specific database access
CREATE ROLE webapp_user WITH LOGIN PASSWORD 'app_password_2026';
-- Create a database administrator role
CREATE ROLE db_admin WITH LOGIN PASSWORD 'admin_password_2026' CREATEDB CREATEROLE;
Each role should have minimal necessary privileges. The reports_reader gets only SELECT permissions. The webapp_user receives INSERT, UPDATE, DELETE as needed.
The db_admin role can create databases and manage other roles without superuser privileges.
Configure connection limits to prevent resource exhaustion:
ALTER ROLE webapp_user CONNECTION LIMIT 20;
ALTER ROLE reports_reader CONNECTION LIMIT 5;
Database-Level Access Control Implementation
PostgreSQL allows precise control over which roles can access specific databases. First, create dedicated databases for different applications:
CREATE DATABASE production_app OWNER webapp_user;
CREATE DATABASE analytics_db OWNER db_admin;
Revoke default public access and grant specific permissions:
-- Remove default public schema access
REVOKE ALL ON DATABASE production_app FROM PUBLIC;
REVOKE ALL ON DATABASE analytics_db FROM PUBLIC;
-- Grant specific access
GRANT CONNECT ON DATABASE production_app TO webapp_user;
GRANT CONNECT ON DATABASE analytics_db TO reports_reader, db_admin;
For multi-tenant applications, create separate schemas within databases:
\c production_app
CREATE SCHEMA tenant_a AUTHORIZATION webapp_user;
CREATE SCHEMA tenant_b AUTHORIZATION webapp_user;
-- Grant schema-specific access
GRANT USAGE ON SCHEMA tenant_a TO webapp_user;
GRANT ALL ON ALL TABLES IN SCHEMA tenant_a TO webapp_user;
This approach provides logical separation without multiple database connections. Your database hosting resources remain efficiently utilized while maintaining security boundaries.
Advanced Permission Management with Group Roles
Group roles simplify permission management across multiple users. Create functional groups first:
-- Create group roles
CREATE ROLE developers;
CREATE ROLE analysts;
CREATE ROLE administrators;
-- Assign database permissions to groups
GRANT CONNECT ON DATABASE production_app TO developers;
GRANT CONNECT ON DATABASE analytics_db TO analysts;
GRANT ALL PRIVILEGES ON DATABASE production_app TO administrators;
Add individual users to appropriate groups:
GRANT developers TO webapp_user;
GRANT analysts TO reports_reader;
GRANT administrators TO db_admin;
Configure default privileges for future objects:
-- Set default table permissions for developers group
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO developers;
-- Set default sequence permissions
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO developers;
New tables automatically inherit proper permissions without manual intervention.
Row-Level Security Configuration
Row-Level Security (RLS) provides fine-grained access control within tables. Enable RLS on sensitive tables:
-- Enable RLS on a user data table
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
-- Create policies for data access
CREATE POLICY user_data_isolation ON user_profiles
FOR ALL TO webapp_user
USING (user_id = current_setting('app.current_user_id')::integer);
Configure your application to set the user context:
-- In application code, set user context
SET app.current_user_id = '12345';
-- Subsequent queries only see relevant rows
SELECT * FROM user_profiles; -- Only returns current user's data
Create separate policies for different operations:
-- Allow users to read their own data
CREATE POLICY user_select_policy ON user_profiles
FOR SELECT TO webapp_user
USING (user_id = current_setting('app.current_user_id')::integer);
-- Allow users to update their own data
CREATE POLICY user_update_policy ON user_profiles
FOR UPDATE TO webapp_user
USING (user_id = current_setting('app.current_user_id')::integer);
Authentication Method Configuration
Configure the pg_hba.conf file to control authentication methods. Location varies by distribution:
# Ubuntu/Debian
sudo nano /etc/postgresql/15/main/pg_hba.conf
# AlmaLinux/RHEL
sudo nano /var/lib/pgsql/15/data/pg_hba.conf
Configure different authentication methods for different connection types:
# Local connections use peer authentication
local all postgres peer
local all all md5
# Network connections require password
host production_app webapp_user 10.0.0.0/8 md5
host analytics_db reports_reader 192.168.1.0/24 md5
# Administrative connections from specific hosts
host all db_admin 192.168.1.100/32 md5
Reload PostgreSQL configuration to apply changes:
sudo systemctl reload postgresql
Test authentication from different sources to verify configuration. Network-based restrictions prevent unauthorized access even with valid credentials.
Monitoring and Auditing Database Access
Enable connection logging to track access patterns:
# Edit postgresql.conf
log_connections = on
log_disconnections = on
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_statement = 'mod'
Create a monitoring role to check active connections:
CREATE ROLE monitor WITH LOGIN PASSWORD 'monitor_password_2026';
GRANT CONNECT ON DATABASE postgres TO monitor;
GRANT pg_monitor TO monitor;
Query active sessions and their activities:
-- Check current connections
SELECT datname, usename, application_name, client_addr, state, query_start
FROM pg_stat_activity
WHERE state = 'active';
-- Monitor failed connection attempts
SELECT * FROM pg_stat_database WHERE datname = 'production_app';
Review our PostgreSQL log analysis tutorial for comprehensive monitoring strategies.
Security Hardening and Best Practices
Implement password policies for database roles:
# Configure password requirements in postgresql.conf
password_encryption = scram-sha-256
shared_preload_libraries = 'passwordcheck'
# Set password complexity requirements
# Add to postgresql.conf after installing passwordcheck extension
passwordcheck.minimum_length = 12
Create a password rotation schedule:
# Script for password rotation
#!/bin/bash
NEW_PASSWORD=$(openssl rand -base64 32)
echo "ALTER ROLE webapp_user PASSWORD '$NEW_PASSWORD';" | sudo -u postgres psql
echo "New password: $NEW_PASSWORD" > /secure/password.log
Limit superuser access by creating administrative roles with specific privileges:
CREATE ROLE backup_admin WITH LOGIN PASSWORD 'backup_password_2026';
GRANT pg_read_all_data TO backup_admin;
CREATE ROLE maintenance_admin WITH LOGIN PASSWORD 'maint_password_2026';
GRANT pg_maintain TO maintenance_admin;
These specialized roles perform specific functions without full superuser access. This reduces security risk.
Automating User Management with Scripts
Create standardized scripts for user provisioning:
#!/bin/bash
# create_app_user.sh
if [ $# -ne 3 ]; then
echo "Usage: $0 username password database"
exit 1
fi
USER=$1
PASS=$2
DB=$3
sudo -u postgres psql << EOF
CREATE ROLE $USER WITH LOGIN PASSWORD '$PASS';
GRANT CONNECT ON DATABASE $DB TO $USER;
GRANT USAGE ON SCHEMA public TO $USER;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $USER;
EOF
echo "User $USER created with access to database $DB"
Automate user cleanup with expiration policies:
# Add expiration metadata
COMMENT ON ROLE temp_user IS 'expires:2026-12-31';
# Script to check and remove expired users
#!/bin/bash
CURRENT_DATE=$(date +%Y-%m-%d)
sudo -u postgres psql -t -c "
SELECT 'DROP ROLE ' || rolname || ';'
FROM pg_roles
WHERE obj_description(oid, 'pg_authid') LIKE 'expires:%'
AND substring(obj_description(oid, 'pg_authid') from 'expires:(.*)') < '$CURRENT_DATE';
" | sudo -u postgres psql
Consider our database automation techniques for additional management strategies.
Proper PostgreSQL user management requires a reliable hosting environment with consistent performance and security. Our VPS hosting solutions provide the stable foundation your database operations need, with full root access and managed support options.
Frequently Asked Questions
How do I recover from accidentally dropping a PostgreSQL user?
If you have a recent backup, restore the pg_authid system catalog. Otherwise, recreate the user with identical permissions. Check pg_depend for objects owned by the dropped user that may need reassignment.
What's the difference between GRANT and ALTER DEFAULT PRIVILEGES?
GRANT applies to existing objects immediately. ALTER DEFAULT PRIVILEGES affects future objects created by specific roles. Use both to ensure complete permission coverage for existing and new database objects.
Can I use LDAP authentication with PostgreSQL role management?
Yes, configure LDAP in pg_hba.conf while maintaining PostgreSQL roles for database-specific permissions. LDAP handles authentication; PostgreSQL roles control database access and permissions.
How do I migrate users between PostgreSQL instances?
Use pg_dumpall --roles-only to export role definitions, then restore on the target instance. Review and adjust connection limits, passwords, and host-specific configurations after migration.
What happens to objects when I drop a role?
PostgreSQL prevents dropping roles that own objects or have privileges on objects. Use REASSIGN OWNED and DROP OWNED commands to transfer ownership and remove privileges before dropping the role.