How to manage PostgreSQL users and databases

How to Manage PostgreSQL Users and Databases Table of Contents 1. [Introduction](#introduction) 2. [Prerequisites](#prerequisites) 3. [Understanding PostgreSQL User Management](#understanding-postgresql-user-management) 4. [Creating and Managing Users](#creating-and-managing-users) 5. [Database Management](#database-management) 6. [Setting Permissions and Privileges](#setting-permissions-and-privileges) 7. [Practical Examples and Use Cases](#practical-examples-and-use-cases) 8. [Security Best Practices](#security-best-practices) 9. [Common Issues and Troubleshooting](#common-issues-and-troubleshooting) 10. [Advanced User and Database Management](#advanced-user-and-database-management) 11. [Monitoring and Maintenance](#monitoring-and-maintenance) 12. [Conclusion](#conclusion) Introduction PostgreSQL is one of the most powerful and feature-rich open-source relational database management systems available today. Effective user and database management is crucial for maintaining security, performance, and organization in any PostgreSQL environment. Whether you're a database administrator managing enterprise systems or a developer working on application databases, understanding how to properly create, configure, and manage PostgreSQL users and databases is essential. This comprehensive guide will walk you through every aspect of PostgreSQL user and database management, from basic creation and configuration to advanced security practices and troubleshooting. You'll learn how to create users with appropriate privileges, manage multiple databases efficiently, implement security best practices, and resolve common issues that arise in production environments. By the end of this article, you'll have the knowledge and practical skills needed to confidently manage PostgreSQL users and databases in any environment, ensuring your systems remain secure, organized, and performant. Prerequisites Before diving into PostgreSQL user and database management, ensure you have: System Requirements - PostgreSQL installed and running (version 10 or later recommended) - Administrative access to the PostgreSQL server - Command line access to the system hosting PostgreSQL - Basic understanding of SQL commands Required Tools - psql: PostgreSQL's command-line interface - pgAdmin (optional): Web-based PostgreSQL administration tool - Text editor for configuration files - Terminal or command prompt access Essential Knowledge - Basic SQL syntax and commands - Understanding of database concepts (tables, schemas, relationships) - Familiarity with command-line interfaces - Basic knowledge of user permissions and security concepts Initial Setup Verification Before proceeding, verify your PostgreSQL installation by connecting as the default superuser: ```bash Connect to PostgreSQL as the postgres user sudo -u postgres psql Or connect with explicit user specification psql -U postgres -h localhost ``` If you can successfully connect and see the PostgreSQL prompt (`postgres=#`), you're ready to begin. Understanding PostgreSQL User Management PostgreSQL User Architecture PostgreSQL uses a role-based security model where users and groups are both represented as "roles." Understanding this architecture is fundamental to effective user management. Key Concepts: - Roles: The fundamental unit of PostgreSQL's security system - Users: Roles that can log in (have the LOGIN attribute) - Groups: Roles used to group other roles for permission management - Superusers: Roles with unrestricted access to the database system - Attributes: Properties that define what a role can do (LOGIN, SUPERUSER, CREATEDB, etc.) Role Hierarchy and Inheritance PostgreSQL supports role inheritance, allowing roles to inherit privileges from other roles they're members of: ```sql -- View current user and available roles SELECT current_user, current_role; -- List all roles in the system \du ``` Authentication Methods PostgreSQL supports various authentication methods configured in the `pg_hba.conf` file: - trust: No authentication required (use carefully) - password: Plain text password authentication - md5: MD5-encrypted password authentication - scram-sha-256: Modern encrypted authentication (recommended) - peer: Use operating system user name - ident: Use operating system user name with mapping Creating and Managing Users Creating Basic Users Creating a Simple User ```sql -- Create a basic user with login capability CREATE USER myuser WITH PASSWORD 'securepassword123'; -- Alternative syntax using CREATE ROLE CREATE ROLE myuser WITH LOGIN PASSWORD 'securepassword123'; ``` Creating Users with Specific Attributes ```sql -- Create user with multiple attributes CREATE USER appuser WITH PASSWORD 'strongpassword456' CREATEDB VALID UNTIL '2025-12-31' CONNECTION LIMIT 10; -- Create a superuser (use with extreme caution) CREATE USER admin_user WITH PASSWORD 'adminsecretpass' SUPERUSER CREATEDB CREATEROLE; ``` User Attributes Explained | Attribute | Description | Example | |-----------|-------------|---------| | LOGIN/NOLOGIN | Can/cannot log in to database | `WITH LOGIN` | | SUPERUSER/NOSUPERUSER | Has/doesn't have superuser privileges | `WITH SUPERUSER` | | CREATEDB/NOCREATEDB | Can/cannot create databases | `WITH CREATEDB` | | CREATEROLE/NOCREATEROLE | Can/cannot create roles | `WITH CREATEROLE` | | INHERIT/NOINHERIT | Inherits/doesn't inherit group privileges | `WITH INHERIT` | | REPLICATION/NOREPLICATION | Can/cannot initiate replication | `WITH REPLICATION` | | BYPASSRLS/NOBYPASSRLS | Can/cannot bypass row level security | `WITH BYPASSRLS` | Modifying Existing Users Changing User Attributes ```sql -- Change user password ALTER USER myuser WITH PASSWORD 'newpassword789'; -- Add database creation privilege ALTER USER myuser WITH CREATEDB; -- Remove login capability ALTER USER myuser WITH NOLOGIN; -- Set password expiration ALTER USER myuser WITH VALID UNTIL '2024-12-31'; -- Change connection limit ALTER USER myuser WITH CONNECTION LIMIT 5; ``` Renaming Users ```sql -- Rename a user ALTER USER oldusername RENAME TO newusername; ``` Deleting Users Safe User Deletion Process Before deleting a user, ensure they don't own any database objects: ```sql -- Check objects owned by user SELECT schemaname, tablename, tableowner FROM pg_tables WHERE tableowner = 'myuser'; -- Check for other owned objects \dp myuser -- Reassign ownership before deletion REASSIGN OWNED BY myuser TO postgres; -- Drop owned objects (if desired) DROP OWNED BY myuser; -- Finally, drop the user DROP USER myuser; ``` Managing User Groups and Role Membership Creating and Managing Groups ```sql -- Create a group role CREATE ROLE developers; -- Add users to the group GRANT developers TO myuser, anotheuser; -- Remove user from group REVOKE developers FROM myuser; -- Create a group with specific privileges CREATE ROLE readonly_users; GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_users; ``` Database Management Creating Databases Basic Database Creation ```sql -- Create a simple database CREATE DATABASE myapp; -- Create database with specific owner CREATE DATABASE myapp WITH OWNER appuser; -- Create database with specific encoding and locale CREATE DATABASE myapp WITH OWNER appuser ENCODING 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8' TEMPLATE template0; ``` Database Creation Parameters | Parameter | Description | Example | |-----------|-------------|---------| | OWNER | Database owner | `OWNER myuser` | | TEMPLATE | Template database | `TEMPLATE template0` | | ENCODING | Character encoding | `ENCODING 'UTF8'` | | LC_COLLATE | Collation order | `LC_COLLATE 'en_US.UTF-8'` | | LC_CTYPE | Character classification | `LC_CTYPE 'en_US.UTF-8'` | | TABLESPACE | Storage tablespace | `TABLESPACE mytablespace` | | CONNECTION LIMIT | Maximum connections | `CONNECTION LIMIT 100` | Database Templates PostgreSQL uses templates for creating new databases: ```sql -- Create database from template1 (default) CREATE DATABASE newdb; -- Create database from template0 (clean template) CREATE DATABASE cleandb WITH TEMPLATE template0; -- Create a custom template CREATE DATABASE mytemplate WITH IS_TEMPLATE true; -- Use custom template CREATE DATABASE projectdb WITH TEMPLATE mytemplate; ``` Modifying Databases Changing Database Properties ```sql -- Rename database ALTER DATABASE oldname RENAME TO newname; -- Change database owner ALTER DATABASE myapp OWNER TO newowner; -- Change connection limit ALTER DATABASE myapp WITH CONNECTION LIMIT 50; -- Set default tablespace ALTER DATABASE myapp SET TABLESPACE newtablespace; ``` Database Configuration Parameters ```sql -- Set database-specific configuration ALTER DATABASE myapp SET timezone TO 'UTC'; ALTER DATABASE myapp SET work_mem TO '256MB'; ALTER DATABASE myapp SET shared_preload_libraries TO 'pg_stat_statements'; -- Reset configuration parameter ALTER DATABASE myapp RESET timezone; ``` Listing and Inspecting Databases Useful Commands for Database Information ```sql -- List all databases \l -- List databases with detailed information \l+ -- Get database size information SELECT datname as database_name, pg_size_pretty(pg_database_size(datname)) as size FROM pg_database WHERE datistemplate = false; -- Show current database SELECT current_database(); -- Show database connection information SELECT datname, numbackends, datconnlimit FROM pg_stat_database WHERE datname IS NOT NULL; ``` Deleting Databases Safe Database Deletion ```sql -- Terminate active connections to database SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'target_database' AND pid <> pg_backend_pid(); -- Drop the database DROP DATABASE target_database; -- Drop database if it exists (PostgreSQL 13+) DROP DATABASE IF EXISTS target_database; ``` Setting Permissions and Privileges Understanding PostgreSQL Privileges PostgreSQL uses a comprehensive privilege system that controls access to various database objects: Object-Level Privileges | Privilege | Applies To | Description | |-----------|------------|-------------| | SELECT | Tables, Views | Read data | | INSERT | Tables | Add new rows | | UPDATE | Tables | Modify existing rows | | DELETE | Tables | Remove rows | | TRUNCATE | Tables | Remove all rows | | REFERENCES | Tables | Create foreign keys | | TRIGGER | Tables | Create triggers | | CREATE | Databases, Schemas | Create objects | | CONNECT | Databases | Connect to database | | TEMPORARY | Databases | Create temporary tables | | EXECUTE | Functions | Execute functions | | USAGE | Schemas, Languages | Use schema or language | Granting Privileges Table-Level Privileges ```sql -- Grant SELECT privilege on a specific table GRANT SELECT ON employees TO myuser; -- Grant multiple privileges on a table GRANT SELECT, INSERT, UPDATE ON employees TO myuser; -- Grant all privileges on a table GRANT ALL PRIVILEGES ON employees TO myuser; -- Grant privileges on all tables in a schema GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user; -- Grant privileges on future tables ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_user; ``` Database-Level Privileges ```sql -- Grant connect privilege to database GRANT CONNECT ON DATABASE myapp TO appuser; -- Grant create privilege (allows creating schemas) GRANT CREATE ON DATABASE myapp TO developer; -- Grant temporary privilege GRANT TEMPORARY ON DATABASE myapp TO temp_user; ``` Schema-Level Privileges ```sql -- Grant usage on schema GRANT USAGE ON SCHEMA myschema TO myuser; -- Grant create privilege on schema GRANT CREATE ON SCHEMA myschema TO developer; -- Grant all privileges on schema GRANT ALL ON SCHEMA myschema TO schema_admin; ``` Revoking Privileges Removing Specific Privileges ```sql -- Revoke SELECT privilege REVOKE SELECT ON employees FROM myuser; -- Revoke multiple privileges REVOKE INSERT, UPDATE ON employees FROM myuser; -- Revoke all privileges REVOKE ALL PRIVILEGES ON employees FROM myuser; -- Revoke database connection REVOKE CONNECT ON DATABASE myapp FROM myuser; ``` Cascade and Restrict Options ```sql -- Revoke with CASCADE (removes dependent privileges) REVOKE CREATE ON SCHEMA myschema FROM myuser CASCADE; -- Revoke with RESTRICT (fails if dependent privileges exist) REVOKE CREATE ON SCHEMA myschema FROM myuser RESTRICT; ``` Row Level Security (RLS) PostgreSQL supports row-level security for fine-grained access control: Enabling RLS ```sql -- Enable RLS on a table ALTER TABLE sensitive_data ENABLE ROW LEVEL SECURITY; -- Create a policy CREATE POLICY user_data_policy ON sensitive_data FOR ALL TO application_users USING (user_id = current_setting('app.current_user_id')::integer); -- Grant table access GRANT SELECT, INSERT, UPDATE ON sensitive_data TO application_users; ``` Checking Privileges Viewing Current Privileges ```sql -- Check table privileges \dp tablename -- Check database privileges \l -- Check schema privileges \dn+ -- Query privilege information SELECT grantee, privilege_type, is_grantable FROM information_schema.table_privileges WHERE table_name = 'employees'; ``` Practical Examples and Use Cases Example 1: Setting Up Application Users This example demonstrates creating users for a typical web application: ```sql -- Create application database CREATE DATABASE ecommerce_app WITH ENCODING 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; -- Connect to the new database \c ecommerce_app -- Create application schema CREATE SCHEMA app; CREATE SCHEMA reporting; -- Create different user types -- 1. Application user (read/write to app schema) CREATE USER app_user WITH PASSWORD 'app_secure_password_2024' CONNECTION LIMIT 20; -- 2. Read-only reporting user CREATE USER report_user WITH PASSWORD 'report_secure_password_2024' CONNECTION LIMIT 5; -- 3. Backup user CREATE USER backup_user WITH PASSWORD 'backup_secure_password_2024' REPLICATION CONNECTION LIMIT 1; -- Grant database connection GRANT CONNECT ON DATABASE ecommerce_app TO app_user, report_user, backup_user; -- Set up application user privileges GRANT USAGE, CREATE ON SCHEMA app TO app_user; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA app TO app_user; ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT ALL ON TABLES TO app_user; -- Set up reporting user privileges GRANT USAGE ON SCHEMA app TO report_user; GRANT SELECT ON ALL TABLES IN SCHEMA app TO report_user; ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT SELECT ON TABLES TO report_user; GRANT USAGE, CREATE ON SCHEMA reporting TO report_user; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA reporting TO report_user; ``` Example 2: Multi-Tenant Application Setup ```sql -- Create multi-tenant database CREATE DATABASE saas_platform; \c saas_platform -- Create tenant-specific schemas CREATE SCHEMA tenant_001; CREATE SCHEMA tenant_002; CREATE SCHEMA shared; -- Create role for each tenant CREATE ROLE tenant_001_users; CREATE ROLE tenant_002_users; CREATE ROLE shared_users; -- Set up tenant isolation GRANT USAGE ON SCHEMA tenant_001 TO tenant_001_users; GRANT ALL ON ALL TABLES IN SCHEMA tenant_001 TO tenant_001_users; GRANT USAGE ON SCHEMA tenant_002 TO tenant_002_users; GRANT ALL ON ALL TABLES IN SCHEMA tenant_002 TO tenant_002_users; -- Shared resources GRANT USAGE ON SCHEMA shared TO tenant_001_users, tenant_002_users; GRANT SELECT ON ALL TABLES IN SCHEMA shared TO tenant_001_users, tenant_002_users; -- Create specific users and assign to tenant roles CREATE USER tenant_001_app WITH PASSWORD 'tenant1_password'; CREATE USER tenant_002_app WITH PASSWORD 'tenant2_password'; GRANT tenant_001_users TO tenant_001_app; GRANT tenant_002_users TO tenant_002_app; GRANT shared_users TO tenant_001_app, tenant_002_app; ``` Example 3: Development Environment Setup ```sql -- Create development database CREATE DATABASE myapp_dev TEMPLATE template0; \c myapp_dev -- Create development users CREATE USER dev_admin WITH PASSWORD 'dev_admin_pass' CREATEDB CREATEROLE; CREATE USER dev_user WITH PASSWORD 'dev_user_pass'; CREATE USER test_user WITH PASSWORD 'test_user_pass' VALID UNTIL '2024-12-31'; -- Grant broad privileges for development GRANT ALL PRIVILEGES ON DATABASE myapp_dev TO dev_admin; GRANT CONNECT, CREATE ON DATABASE myapp_dev TO dev_user; GRANT CONNECT ON DATABASE myapp_dev TO test_user; -- Set up schema privileges GRANT ALL ON SCHEMA public TO dev_admin, dev_user; GRANT USAGE ON SCHEMA public TO test_user; ``` Security Best Practices Password Security Strong Password Policies ```sql -- Create users with strong passwords CREATE USER secure_user WITH PASSWORD 'Str0ng!P@ssw0rd#2024$'; -- Set password expiration ALTER USER secure_user VALID UNTIL '2024-12-31'; -- Force password change by setting past expiration ALTER USER secure_user VALID UNTIL '2023-01-01'; ``` Password Encryption Configuration Edit `postgresql.conf` to ensure secure password storage: ```conf Use SCRAM-SHA-256 for password encryption password_encryption = scram-sha-256 ``` Update `pg_hba.conf` for secure authentication: ```conf Use scram-sha-256 authentication method host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 ``` Principle of Least Privilege Implementing Minimal Permissions ```sql -- Create role-based access control CREATE ROLE app_readers; CREATE ROLE app_writers; CREATE ROLE app_admins; -- Grant minimal necessary privileges GRANT CONNECT ON DATABASE myapp TO app_readers; GRANT USAGE ON SCHEMA public TO app_readers; GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readers; -- Writers inherit readers' privileges plus write access GRANT app_readers TO app_writers; GRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_writers; -- Admins get broader privileges GRANT app_writers TO app_admins; GRANT CREATE ON SCHEMA public TO app_admins; ``` Connection Security Limiting Connections ```sql -- Set connection limits per user ALTER USER app_user CONNECTION LIMIT 10; ALTER USER report_user CONNECTION LIMIT 3; ALTER USER batch_user CONNECTION LIMIT 1; -- Set database-wide connection limit ALTER DATABASE myapp CONNECTION LIMIT 50; ``` IP Address Restrictions Configure `pg_hba.conf` for IP-based access control: ```conf Allow specific IP ranges host myapp app_user 192.168.1.0/24 scram-sha-256 host myapp report_user 10.0.1.100/32 scram-sha-256 Deny all other connections host all all 0.0.0.0/0 reject ``` Auditing and Monitoring Enable Connection Logging In `postgresql.conf`: ```conf Log connections and disconnections log_connections = on log_disconnections = on Log failed authentication attempts log_statement = 'all' log_min_duration_statement = 1000 Log user activities log_line_prefix = '%t [%p-%l] %q%u@%d ' ``` Monitor User Activity ```sql -- Check current connections SELECT pid, usename, application_name, client_addr, backend_start, state FROM pg_stat_activity WHERE state = 'active'; -- Monitor failed login attempts (requires logging) -- Check PostgreSQL logs for authentication failures ``` Regular Security Maintenance Periodic Security Review ```sql -- Review user privileges periodically SELECT r.rolname, r.rolsuper, r.rolinherit, r.rolcreaterole, r.rolcreatedb, r.rolcanlogin, r.rolconnlimit, r.rolvaliduntil FROM pg_roles r WHERE r.rolcanlogin = true; -- Check for unused accounts SELECT usename, MAX(backend_start) as last_login FROM pg_stat_activity GROUP BY usename ORDER BY last_login DESC NULLS LAST; ``` Common Issues and Troubleshooting Authentication Problems Issue: "FATAL: password authentication failed" Possible Causes and Solutions: 1. Incorrect Password: ```sql -- Reset user password ALTER USER myuser WITH PASSWORD 'newpassword'; ``` 2. Authentication Method Mismatch: Check and update `pg_hba.conf`: ```conf Change from 'md5' to 'scram-sha-256' if needed host all all 127.0.0.1/32 scram-sha-256 ``` 3. User Doesn't Exist: ```sql -- Check if user exists SELECT rolname FROM pg_roles WHERE rolname = 'myuser'; -- Create user if missing CREATE USER myuser WITH PASSWORD 'password'; ``` Issue: "FATAL: role does not exist" ```sql -- List all roles to verify \du -- Create the missing role CREATE ROLE missing_role WITH LOGIN PASSWORD 'password'; ``` Permission Denied Errors Issue: "ERROR: permission denied for table" Diagnosis and Resolution: ```sql -- Check current privileges \dp tablename -- Check user's role memberships SELECT r.rolname as role, m.rolname as member FROM pg_roles r JOIN pg_auth_members am ON r.oid = am.roleid JOIN pg_roles m ON am.member = m.oid WHERE m.rolname = 'username'; -- Grant necessary privileges GRANT SELECT ON tablename TO username; ``` Issue: "ERROR: permission denied for schema" ```sql -- Grant schema usage GRANT USAGE ON SCHEMA schemaname TO username; -- Grant create privileges if needed GRANT CREATE ON SCHEMA schemaname TO username; ``` Connection Issues Issue: "FATAL: too many connections" Solutions: 1. Check current connections: ```sql SELECT count(*) FROM pg_stat_activity; ``` 2. Increase max_connections (requires restart): ```conf In postgresql.conf max_connections = 200 ``` 3. Implement connection pooling: Use tools like PgBouncer or connection pooling in application 4. Set user-specific limits: ```sql ALTER USER heavy_user CONNECTION LIMIT 5; ``` Issue: "FATAL: database does not exist" ```sql -- List available databases \l -- Create the database if needed CREATE DATABASE missing_database; -- Check if user has CONNECT privilege GRANT CONNECT ON DATABASE database_name TO username; ``` Database Dropping Issues Issue: "ERROR: database is being accessed by other users" ```sql -- Find active connections SELECT pid, usename, application_name, state FROM pg_stat_activity WHERE datname = 'database_to_drop'; -- Terminate connections SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'database_to_drop' AND pid <> pg_backend_pid(); -- Now drop the database DROP DATABASE database_to_drop; ``` User Deletion Issues Issue: "ERROR: role cannot be dropped because some objects depend on it" ```sql -- Find objects owned by the user SELECT schemaname, tablename, tableowner FROM pg_tables WHERE tableowner = 'user_to_drop'; -- Check for granted privileges SELECT * FROM information_schema.table_privileges WHERE grantee = 'user_to_drop'; -- Reassign ownership REASSIGN OWNED BY user_to_drop TO postgres; -- Drop owned objects DROP OWNED BY user_to_drop; -- Now drop the user DROP USER user_to_drop; ``` Performance Issues Issue: Slow privilege checks ```sql -- Update statistics ANALYZE; -- Rebuild system catalog statistics VACUUM ANALYZE pg_class; VACUUM ANALYZE pg_attribute; ``` Advanced User and Database Management Role-Based Access Control (RBAC) Implementing Hierarchical Roles ```sql -- Create hierarchical role structure CREATE ROLE company_users; CREATE ROLE department_managers; CREATE ROLE system_administrators; -- Set up inheritance hierarchy GRANT company_users TO department_managers; GRANT department_managers TO system_administrators; -- Define base privileges GRANT CONNECT ON DATABASE company_db TO company_users; GRANT USAGE ON SCHEMA public TO company_users; GRANT SELECT ON ALL TABLES IN SCHEMA public TO company_users; -- Add management privileges GRANT INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO department_managers; GRANT DELETE ON sensitive_tables TO department_managers; -- Add administrative privileges GRANT CREATE ON SCHEMA public TO system_administrators; GRANT ALL PRIVILEGES ON DATABASE company_db TO system_administrators; -- Assign users to roles CREATE USER john_doe WITH PASSWORD 'secure_pass'; CREATE USER jane_manager WITH PASSWORD 'manager_pass'; CREATE USER admin_user WITH PASSWORD 'admin_pass'; GRANT company_users TO john_doe; GRANT department_managers TO jane_manager; GRANT system_administrators TO admin_user; ``` Dynamic Privilege Management Using Functions for Privilege Management ```sql -- Create function to grant standard user privileges CREATE OR REPLACE FUNCTION grant_standard_user_access(username TEXT, dbname TEXT) RETURNS VOID AS $$ BEGIN EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', dbname, username); EXECUTE format('GRANT USAGE ON SCHEMA public TO %I', username); EXECUTE format('GRANT SELECT ON ALL TABLES IN SCHEMA public TO %I', username); EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO %I', username); END; $$ LANGUAGE plpgsql; -- Use the function SELECT grant_standard_user_access('newuser', 'myapp'); ``` Database Templating and Cloning Creating Standardized Database Templates ```sql -- Create a template database CREATE DATABASE app_template WITH IS_TEMPLATE true; -- Connect and set up template structure \c app_template -- Create standard schemas CREATE SCHEMA app_data; CREATE SCHEMA app_config; CREATE SCHEMA app_logs; -- Create standard tables CREATE TABLE app_config.settings ( key VARCHAR(100) PRIMARY KEY, value TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Set up standard roles CREATE ROLE app_template_users; CREATE ROLE app_template_admins; -- Grant template privileges GRANT CONNECT ON DATABASE app_template TO app_template_users; GRANT USAGE ON ALL SCHEMAS IN DATABASE app_template TO app_template_users; -- Create new databases from template CREATE DATABASE production_app WITH TEMPLATE app_template; CREATE DATABASE staging_app WITH TEMPLATE app_template; ``` Advanced Security Features Implementing Row Level Security with Dynamic Policies ```sql -- Create table with RLS CREATE TABLE user_data ( id SERIAL PRIMARY KEY, user_id INTEGER, data TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Enable RLS ALTER TABLE user_data ENABLE ROW LEVEL SECURITY; -- Create dynamic policy using session variables CREATE POLICY user_isolation_policy ON user_data FOR ALL TO application_role USING (user_id = current_setting('app.current_user_id', true)::INTEGER); -- Create function to set user context CREATE OR REPLACE FUNCTION set_user_context(user_id INTEGER) RETURNS VOID AS $$ BEGIN PERFORM set_config('app.current_user_id', user_id::TEXT, false); END; $$ LANGUAGE plpgsql SECURITY DEFINER; -- Usage example SELECT set_user_context(123); SELECT * FROM user_data; -- Only returns data for user 123 ``` Monitoring and Maintenance User Activity Monitoring Creating Monitoring Views ```sql -- Create view for user activity monitoring CREATE VIEW user_activity_summary AS SELECT usename, application_name, client_addr, backend_start, state, query_start, state_change FROM pg_stat_activity WHERE usename IS NOT NULL; -- Create view for connection statistics CREATE VIEW user_connection_stats AS SELECT usename, COUNT(*) as active_connections, MAX(backend_start) as last_connection, string_agg(DISTINCT application_name, ', ') as applications FROM pg_stat_activity WHERE usename IS NOT NULL GROUP BY usename; ``` Automated Maintenance Tasks User Cleanup Procedures ```sql -- Create procedure to clean up expired users CREATE OR REPLACE FUNCTION cleanup_expired_users() RETURNS INTEGER AS $$ DECLARE expired_user RECORD; cleanup_count INTEGER := 0; BEGIN -- Find expired users FOR expired_user IN SELECT rolname FROM pg_roles WHERE rolvaliduntil < CURRENT_TIMESTAMP AND rolcanlogin = true LOOP -- Disable login for expired users EXECUTE format('ALTER USER %I WITH NOLOGIN', expired_user.rolname); cleanup_count := cleanup_count + 1; -- Log the action RAISE NOTICE 'Disabled login for expired user: %', expired_user.rolname; END LOOP; RETURN cleanup_count; END; $$ LANGUAGE plpgsql; -- Schedule regular cleanup (using pg_cron extension) -- SELECT cron.schedule('cleanup-expired-users', '0 2 *', 'SELECT cleanup_expired_users();'); ``` Database Health Monitoring Creating Health Check Functions ```sql -- Database size monitoring CREATE VIEW database_size_monitor AS SELECT datname as database_name, pg_size_pretty(pg_database_size(datname)) as size, pg_database_size(datname) as size_bytes FROM pg_database WHERE datistemplate = false ORDER BY pg_database_size(datname) DESC; -- User privilege audit view CREATE VIEW user_privilege_audit AS SELECT r.rolname as username, r.rolsuper as is_superuser, r.rolcreaterole as can_create_roles, r.rolcreatedb as can_create_databases, r.rolcanlogin as can_login, r.rolconnlimit as connection_limit, r.rolvaliduntil as valid_until, CASE WHEN r.rolvaliduntil < CURRENT_TIMESTAMP THEN 'EXPIRED' WHEN r.rolvaliduntil IS NULL THEN 'NEVER EXPIRES' ELSE 'VALID' END as status FROM pg_roles r WHERE r.rolcanlogin = true ORDER BY r.rolname; -- Connection monitoring function CREATE OR REPLACE FUNCTION monitor_user_connections() RETURNS TABLE ( username TEXT, current_connections BIGINT, max_connections INTEGER, connection_usage_percent NUMERIC ) AS $$ BEGIN RETURN QUERY SELECT r.rolname::TEXT, COALESCE(c.connection_count, 0) as current_connections, CASE WHEN r.rolconnlimit = -1 THEN (SELECT setting::INTEGER FROM pg_settings WHERE name = 'max_connections') ELSE r.rolconnlimit END as max_connections, CASE WHEN r.rolconnlimit = -1 THEN (COALESCE(c.connection_count, 0) * 100.0 / (SELECT setting::INTEGER FROM pg_settings WHERE name = 'max_connections')) ELSE (COALESCE(c.connection_count, 0) * 100.0 / r.rolconnlimit) END as connection_usage_percent FROM pg_roles r LEFT JOIN ( SELECT usename, COUNT(*) as connection_count FROM pg_stat_activity WHERE usename IS NOT NULL GROUP BY usename ) c ON r.rolname = c.usename WHERE r.rolcanlogin = true ORDER BY connection_usage_percent DESC; END; $$ LANGUAGE plpgsql; ``` Performance Optimization Monitoring and Optimizing User Activities ```sql -- Create function to analyze slow queries by user CREATE OR REPLACE FUNCTION analyze_user_performance() RETURNS TABLE ( username TEXT, avg_query_time NUMERIC, total_queries BIGINT, slow_queries BIGINT ) AS $$ BEGIN RETURN QUERY SELECT usename::TEXT, AVG(EXTRACT(EPOCH FROM (now() - query_start)))::NUMERIC(10,2) as avg_query_time, COUNT(*) as total_queries, COUNT(*) FILTER (WHERE EXTRACT(EPOCH FROM (now() - query_start)) > 60) as slow_queries FROM pg_stat_activity WHERE state = 'active' AND usename IS NOT NULL AND query_start IS NOT NULL GROUP BY usename ORDER BY avg_query_time DESC; END; $$ LANGUAGE plpgsql; -- Usage example SELECT * FROM analyze_user_performance(); ``` Backup and Recovery for User Management Backing Up User and Role Information ```bash Export all roles and their privileges pg_dumpall -U postgres --roles-only > roles_backup.sql Export specific database with user privileges pg_dump -U postgres --clean --create --if-exists myapp > myapp_with_privileges.sql Restore roles psql -U postgres -f roles_backup.sql Restore database with privileges psql -U postgres -f myapp_with_privileges.sql ``` Creating Role Management Scripts ```sql -- Create a comprehensive backup of all user information CREATE OR REPLACE FUNCTION backup_user_info() RETURNS TEXT AS $$ DECLARE backup_info TEXT := ''; user_record RECORD; BEGIN backup_info := backup_info || '-- PostgreSQL User Backup Generated: ' || CURRENT_TIMESTAMP || E'\n\n'; FOR user_record IN SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolvaliduntil FROM pg_roles WHERE rolcanlogin = true ORDER BY rolname LOOP backup_info := backup_info || format('CREATE USER %I WITH', user_record.rolname); IF user_record.rolsuper THEN backup_info := backup_info || ' SUPERUSER'; END IF; IF user_record.rolcreaterole THEN backup_info := backup_info || ' CREATEROLE'; END IF; IF user_record.rolcreatedb THEN backup_info := backup_info || ' CREATEDB'; END IF; IF user_record.rolreplication THEN backup_info := backup_info || ' REPLICATION'; END IF; IF user_record.rolbypassrls THEN backup_info := backup_info || ' BYPASSRLS'; END IF; IF user_record.rolconnlimit != -1 THEN backup_info := backup_info || format(' CONNECTION LIMIT %s', user_record.rolconnlimit); END IF; IF user_record.rolvaliduntil IS NOT NULL THEN backup_info := backup_info || format(' VALID UNTIL %L', user_record.rolvaliduntil); END IF; backup_info := backup_info || ';' || E'\n'; END LOOP; RETURN backup_info; END; $$ LANGUAGE plpgsql; -- Use the backup function SELECT backup_user_info(); ``` Conclusion Effective PostgreSQL user and database management is a critical skill for maintaining secure, organized, and high-performing database systems. Throughout this comprehensive guide, we've covered the essential aspects of PostgreSQL administration, from basic user creation to advanced security implementations and monitoring strategies. Key Takeaways User Management Fundamentals: - PostgreSQL uses a role-based security model where users and groups are both represented as roles - Understanding role attributes (LOGIN, SUPERUSER, CREATEDB, etc.) is crucial for proper access control - User creation should always follow the principle of least privilege Database Administration Best Practices: - Use database templates for standardized deployments - Implement proper naming conventions and organizational structures - Regular maintenance and monitoring are essential for optimal performance Security Implementation: - Always use strong passwords and modern authentication methods (SCRAM-SHA-256) - Implement role-based access control (RBAC) for complex environments - Use Row Level Security (RLS) for fine-grained data access control - Regular security audits and user privilege reviews prevent unauthorized access Troubleshooting and Maintenance: - Understanding common error messages and their solutions saves valuable time - Automated monitoring and maintenance procedures ensure system reliability - Proper backup strategies for both data and user configurations are essential Moving Forward As you implement these PostgreSQL user and database management practices, remember that security and organization should be built into your processes from the beginning. Start with a solid foundation of role-based access control, implement monitoring early, and maintain regular review cycles for user privileges and database health. The examples and scripts provided in this guide can be adapted to your specific environment and requirements. Whether you're managing a small application database or a large enterprise system, these principles and practices will help ensure your PostgreSQL environment remains secure, performant, and well-organized. Continue to stay updated with PostgreSQL releases and security best practices, as the database system continues to evolve with new features and improvements. Regular training and keeping up with the PostgreSQL community will help you maintain expertise in this powerful database management system. By following the guidelines and best practices outlined in this comprehensive guide, you'll be well-equipped to handle any PostgreSQL user and database management challenge that comes your way, ensuring your systems remain robust, secure, and efficiently managed.