Install PostgreSQL on Ubuntu server
PostgreSQL is a powerful, open-source object-relational database management system (ORDBMS) that uses and extends the SQL language. It's known for its reliability, data integrity, and robust feature set including ACID compliance, complex queries, foreign keys, triggers, updatable views, transactional integrity, and multiversion concurrency control (MVCC).
System Requirements
Before installation, ensure your Ubuntu server meets these minimum requirements:
- Disk Space: At least 100 MB for basic installation, plus space for your databases
- RAM: Minimum 256 MB, recommended 1 GB or more for production workloads
- CPU: Any modern processor; performance scales with cores for concurrent queries
- Supported Ubuntu Versions: 18.04 LTS, 20.04 LTS, 22.04 LTS, 24.04 LTS
1sudo apt update2sudo apt install postgresql postgresql-clientThis command installs:
- postgresql: The core database server package
- postgresql-client: Command-line client tools for interacting with PostgreSQL
- postgresql-contrib: Additional utilities and extensions (automatically included as dependency)
The installation process automatically:
- Creates a
postgressystem user and group - Initializes a database cluster in
/var/lib/postgresql/<version>/main/ - Starts the PostgreSQL service
- Enables the service to start on boot
Verify Installation and Service Status
Check if PostgreSQL service is running:
1sudo systemctl status postgresqlView PostgreSQL service logs for troubleshooting:
1sudo journalctl -u postgresql -n 50 --no-pagerCheck PostgreSQL version
1psql --versionYou can also check the server version while connected:
1sudo -u postgres psql -c "SELECT version();"Configure PostgreSQL for Remote Access
By default, PostgreSQL only accepts connections from localhost for security reasons. To enable remote access, you need to modify two configuration files.
Understanding postgresql.conf
The postgresql.conf file contains server-wide configuration parameters. Key sections include:
- Connection Settings: Network interfaces, port, max connections
- Resource Usage: Memory, disk, kernel resources
- Write-Ahead Log: WAL settings for durability and replication
- Query Tuning: Planner and execution parameters
1sudo nano /etc/postgresql/14/main/postgresql.conf #check version folder2listen_addresses = '*'Important Configuration Parameters to Consider:
1# Connection Settings2listen_addresses = '*' # Listen on all network interfaces3port = 5432 # Default PostgreSQL port4max_connections = 100 # Maximum concurrent connections5
6# Memory Settings (adjust based on available RAM)7shared_buffers = 256MB # RAM used for caching data8effective_cache_size = 1GB # Estimate of OS + PostgreSQL cache9work_mem = 4MB # Memory per query operation10maintenance_work_mem = 64MB # Memory for maintenance operations11
12# Write-Ahead Log13wal_level = replica # Minimal, replica, or logical14max_wal_size = 1GB # Maximum WAL size before checkpointAfter editing, restart PostgreSQL to apply changes.
Understanding pg_hba.conf (Host-Based Authentication)
The pg_hba.conf file controls client authentication. Each line specifies:
- Connection Type:
local(Unix socket),host(TCP/IP),hostssl(SSL/TLS only) - Database: Which database(s) the rule applies to
- User: Which user(s) can connect
- Address: IP address or CIDR range (for TCP/IP connections)
- Authentication Method: How to authenticate (trust, md5, scram-sha-256, peer, etc.)
Authentication Methods Explained:
peer: Uses OS username (local connections only)md5: MD5-hashed password (legacy, less secure)scram-sha-256: Modern password authentication (recommended)trust: No authentication (dangerous, use only for testing)reject: Always reject connection
Create template
1sudo -u postgres psql template12ALTER USER postgres with encrypted password 'password';3
4sudo nano /etc/postgresql/14/main/pg_hba.conf5#add line with LAN subnet6hostssl template1 postgres 0.0.0.0/0 scram-sha-2567#or8#allow all to all9host all all 0.0.0.0/0 md5Security Best Practices for pg_hba.conf:
Instead of allowing all IPs (0.0.0.0/0), restrict access to specific networks:
1# Allow from specific IP2host all all 192.168.1.100/32 scram-sha-2563
4# Allow from subnet (e.g., local network)5host all all 192.168.1.0/24 scram-sha-2566
7# Require SSL for remote connections8hostssl all all 0.0.0.0/0 scram-sha-2569
10# Local connections use peer authentication11local all postgres peerThe order of rules matters - PostgreSQL uses the first matching rule. Place more specific rules before general ones.
1sudo systemctl restart postgresqlConfigure Firewall
If using UFW (Uncomplicated Firewall), allow PostgreSQL traffic:
1sudo ufw allow 5432/tcp2sudo ufw statusFor more specific access control:
1# Allow from specific IP2sudo ufw allow from 192.168.1.100 to any port 54323
4# Allow from subnet5sudo ufw allow from 192.168.1.0/24 to any port 5432Check connection to db
1psql --host 10.0.0.144 --username postgres --password --dbname template1Alternative connection string formats:
1# Using connection URI2psql postgresql://postgres:[email protected]:5432/template13
4# Using environment variables5export PGHOST=10.0.0.1446export PGUSER=postgres7export PGDATABASE=template18psql9
10# Test connection without interactive shell11psql -h 10.0.0.144 -U postgres -d template1 -c "SELECT current_database(), current_user;"Connection Troubleshooting
If connection fails, check these common issues:
1. Service not running:
1sudo systemctl status postgresql2sudo systemctl start postgresql2. Port not listening:
1sudo ss -tunlp | grep 54322# Should show: LISTEN 0.0.0.0:54323. Firewall blocking:
1sudo ufw status2sudo iptables -L -n | grep 54324. Check PostgreSQL logs:
1sudo tail -f /var/log/postgresql/postgresql-14-main.logCreate new database with user
1sudo -u postgres psql2CREATE DATABASE db;3CREATE USER usr WITH ENCRYPTED PASSWORD '123';4GRANT ALL PRIVILEGES ON DATABASE db TO usr;Database and User Management
Understanding PostgreSQL Roles and Privileges
PostgreSQL uses "roles" for authentication. A role can be a user or a group. Key attributes:
- LOGIN: Role can connect to database
- SUPERUSER: Bypass all permission checks
- CREATEDB: Can create databases
- CREATEROLE: Can create other roles
- REPLICATION: Can initiate streaming replication
Advanced User Creation Examples
Create user with specific privileges:
1# User with database creation privilege2CREATE USER developer WITH ENCRYPTED PASSWORD 'dev_pass' CREATEDB;3
4# User with limited connection5CREATE USER readonly WITH ENCRYPTED PASSWORD 'read_pass' CONNECTION LIMIT 5;6
7# User with expiration date8CREATE USER temp_user WITH ENCRYPTED PASSWORD 'temp_pass' VALID UNTIL '2026-12-31';9
10# Create role (group) and add users to it11CREATE ROLE app_users;12GRANT CONNECT ON DATABASE db TO app_users;13GRANT app_users TO usr;Grant Specific Schema Privileges
For better security, grant privileges at schema and table levels:
1# Connect to the database2\c db3
4# Grant schema usage5GRANT USAGE ON SCHEMA public TO usr;6
7# Grant SELECT on all existing tables8GRANT SELECT ON ALL TABLES IN SCHEMA public TO usr;9
10# Grant SELECT on all future tables11ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO usr;12
13# Grant specific privileges on specific table14GRANT SELECT, INSERT, UPDATE ON specific_table TO usr;15
16# Grant all privileges on schema17GRANT ALL PRIVILEGES ON SCHEMA public TO usr;18GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO usr;19GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO usr;Database Management Commands
Useful commands for database administration:
1# List all databases2\l3# or4SELECT datname FROM pg_database;5
6# List all users/roles7\du8# or9SELECT rolname, rolsuper, rolcreatedb, rolcanlogin FROM pg_roles;10
11# Check database size12SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) 13FROM pg_database ORDER BY pg_database_size(pg_database.datname) DESC;14
15# Check table sizes in current database16SELECT schemaname, tablename, 17 pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size18FROM pg_tables 19ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;20
21# Show active connections22SELECT pid, usename, application_name, client_addr, state, query_start 23FROM pg_stat_activity 24WHERE state = 'active';25
26# Kill a connection27SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid = 12345;Backup and Restore
Backup Strategies
1. Logical Backup with pg_dump:
1# Backup single database2pg_dump -h localhost -U postgres -d db -F c -f /backup/db_backup.dump3
4# Backup with compression5pg_dump -h localhost -U postgres -d db -F c -Z 9 -f /backup/db_compressed.dump6
7# Backup as SQL script8pg_dump -h localhost -U postgres -d db -f /backup/db_backup.sql9
10# Backup specific tables11pg_dump -h localhost -U postgres -d db -t table1 -t table2 -f /backup/tables.sql12
13# Backup all databases14pg_dumpall -h localhost -U postgres -f /backup/all_databases.sql15
16# Backup only schema (no data)17pg_dump -h localhost -U postgres -d db --schema-only -f /backup/schema.sql18
19# Backup only data (no schema)20pg_dump -h localhost -U postgres -d db --data-only -f /backup/data.sql2. Physical Backup with pg_basebackup:
1# Create base backup for PITR (Point-in-Time Recovery)2pg_basebackup -h localhost -U postgres -D /backup/base -F tar -z -PRestore Operations
1# Restore from custom format dump2pg_restore -h localhost -U postgres -d db -c /backup/db_backup.dump3
4# Restore from SQL script5psql -h localhost -U postgres -d db -f /backup/db_backup.sql6
7# Restore all databases8psql -h localhost -U postgres -f /backup/all_databases.sql9
10# Restore with verbose output and error handling11pg_restore -h localhost -U postgres -d db -v --single-transaction /backup/db_backup.dumpAutomated Backup Script
Create a daily backup script:
1# Create backup script2sudo nano /usr/local/bin/postgres_backup.shAdd the following content:
1#!/bin/bash2BACKUP_DIR="/backup/postgresql"3TIMESTAMP=$(date +%Y%m%d_%H%M%S)4DAYS_TO_KEEP=75
6mkdir -p $BACKUP_DIR7
8# Backup all databases9pg_dumpall -U postgres | gzip > $BACKUP_DIR/all_databases_$TIMESTAMP.sql.gz10
11# Remove old backups12find $BACKUP_DIR -name "all_databases_*.sql.gz" -mtime +$DAYS_TO_KEEP -delete13
14# Log completion15echo "$(date): Backup completed successfully" >> /var/log/postgres_backup.logMake it executable and schedule:
1sudo chmod +x /usr/local/bin/postgres_backup.sh2
3# Add to crontab (runs daily at 2 AM)4sudo crontab -e5# Add line:60 2 * * * /usr/local/bin/postgres_backup.shPerformance Tuning
Memory Configuration
Adjust based on available system RAM:
1# For system with 4GB RAM2shared_buffers = 1GB # 25% of RAM3effective_cache_size = 3GB # 75% of RAM4maintenance_work_mem = 256MB # For VACUUM, CREATE INDEX5work_mem = 10MB # Per operation6
7# For system with 16GB RAM8shared_buffers = 4GB9effective_cache_size = 12GB10maintenance_work_mem = 1GB11work_mem = 20MBConnection Pool Configuration
1max_connections = 100 # Maximum concurrent connections2superuser_reserved_connections = 3 # Reserved for superuser3
4# Consider using connection pooler (PgBouncer) for high-traffic applicationsQuery Performance Monitoring
1# Enable slow query logging2log_min_duration_statement = 1000 # Log queries taking > 1 second3
4# Show query execution time5\timing on6
7# Analyze query performance8EXPLAIN ANALYZE SELECT * FROM large_table WHERE id = 12345;9
10# Find slow queries11SELECT query, calls, total_time, mean_time, max_time12FROM pg_stat_statements13ORDER BY mean_time DESC14LIMIT 10;Vacuum and Analyze
Regular maintenance is crucial for performance:
1# Manual vacuum2VACUUM VERBOSE tablename;3
4# Aggressive vacuum to reclaim space5VACUUM FULL tablename;6
7# Analyze table statistics for query planner8ANALYZE tablename;9
10# Vacuum and analyze together11VACUUM ANALYZE tablename;12
13# Enable autovacuum (should be enabled by default)14autovacuum = on15autovacuum_max_workers = 316autovacuum_naptime = 1minSecurity Hardening
SSL/TLS Configuration
Enable encrypted connections:
1# Generate self-signed certificate (for testing)2sudo openssl req -new -x509 -days 365 -nodes -text -out /etc/postgresql/14/main/server.crt -keyout /etc/postgresql/14/main/server.key -subj "/CN=postgresql"3
4sudo chown postgres:postgres /etc/postgresql/14/main/server.{crt,key}5sudo chmod 600 /etc/postgresql/14/main/server.key6
7# Enable SSL in postgresql.conf8ssl = on9ssl_cert_file = '/etc/postgresql/14/main/server.crt'10ssl_key_file = '/etc/postgresql/14/main/server.key'11
12# Require SSL in pg_hba.conf13hostssl all all 0.0.0.0/0 scram-sha-256Password Policies
1# Set password encryption2password_encryption = scram-sha-2563
4# Enforce strong passwords (install pgcrypto extension)5CREATE EXTENSION IF NOT EXISTS pgcrypto;6
7# Check password strength before setting8SELECT password_hash = crypt('newpassword', gen_salt('bf'));Audit Logging
1# Enable comprehensive logging2log_connections = on3log_disconnections = on4log_duration = on5log_line_prefix = '%t [%p]: user=%u,db=%d,app=%a,client=%h '6log_statement = 'all' # Log all statements (verbose)7# or8log_statement = 'ddl' # Log only DDL statementsExtensions and Additional Features
Commonly Used Extensions
1# Connect to database2sudo -u postgres psql -d db3
4# Install popular extensions5CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; # UUID generation6CREATE EXTENSION IF NOT EXISTS "pg_trgm"; # Trigram text search7CREATE EXTENSION IF NOT EXISTS "hstore"; # Key-value store8CREATE EXTENSION IF NOT EXISTS "pgcrypto"; # Cryptographic functions9CREATE EXTENSION IF NOT EXISTS "pg_stat_statements"; # Query statistics10
11# List installed extensions12\dx13
14# Get extension details15\dx+ pg_trgmFull-Text Search Example
1# Create table with full-text search2CREATE TABLE articles (3 id SERIAL PRIMARY KEY,4 title TEXT,5 body TEXT,6 search_vector tsvector7);8
9# Create index for fast searching10CREATE INDEX idx_fts ON articles USING gin(search_vector);11
12# Update search vector automatically13CREATE TRIGGER tsvector_update BEFORE INSERT OR UPDATE ON articles14FOR EACH ROW EXECUTE FUNCTION15tsvector_update_trigger(search_vector, 'pg_catalog.english', title, body);16
17# Search example18SELECT title FROM articles WHERE search_vector @@ to_tsquery('postgresql & database');Monitoring and Maintenance
System Catalog Queries
1# Check database activity2SELECT * FROM pg_stat_activity;3
4# Check table statistics5SELECT * FROM pg_stat_user_tables;6
7# Check index usage8SELECT schemaname, tablename, indexname, idx_scan9FROM pg_stat_user_indexes10ORDER BY idx_scan ASC;11
12# Find unused indexes13SELECT schemaname, tablename, indexname14FROM pg_stat_user_indexes15WHERE idx_scan = 0 AND indexname NOT LIKE 'pg_toast%';16
17# Check cache hit ratio (should be > 90%)18SELECT 19 sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) AS cache_hit_ratio20FROM pg_statio_user_tables;Health Check Script
1# Create health check script2cat > /usr/local/bin/pg_health_check.sh << 'EOF'3#!/bin/bash4echo "=== PostgreSQL Health Check ==="5echo "Service Status:"6systemctl status postgresql --no-pager | grep Active7
8echo -e "\nDisk Usage:"9df -h /var/lib/postgresql10
11echo -e "\nDatabase Sizes:"12sudo -u postgres psql -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC;"13
14echo -e "\nActive Connections:"15sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity WHERE state = 'active';"16
17echo -e "\nCache Hit Ratio:"18sudo -u postgres psql -c "SELECT sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) AS cache_hit_ratio FROM pg_statio_user_tables;"19EOF20
21chmod +x /usr/local/bin/pg_health_check.shCommon Operations Reference
Quick Command Reference
1# Start/Stop/Restart service2sudo systemctl start postgresql3sudo systemctl stop postgresql4sudo systemctl restart postgresql5sudo systemctl reload postgresql # Reload config without restart6
7# Check PostgreSQL process8ps aux | grep postgres9
10# Connect as postgres user11sudo -i -u postgres12psql13
14# Execute SQL from command line15psql -U postgres -d db -c "SELECT version();"16
17# Import CSV data18\copy tablename FROM '/path/to/file.csv' DELIMITER ',' CSV HEADER;19
20# Export to CSV21\copy (SELECT * FROM tablename) TO '/path/to/output.csv' WITH CSV HEADER;22
23# Change database owner24ALTER DATABASE db OWNER TO new_owner;25
26# Rename database27ALTER DATABASE old_name RENAME TO new_name;28
29# Drop database (with active connections)30SELECT pg_terminate_backend(pg_stat_activity.pid)31FROM pg_stat_activity32WHERE pg_stat_activity.datname = 'db' AND pid <> pg_backend_pid();33DROP DATABASE db;Resources and Further Reading
- Official Documentation: https://www.postgresql.org/docs/
- PostgreSQL Wiki: https://wiki.postgresql.org/
- Performance Tuning: PGTune (https://pgtune.leopard.in.ua/)
- Monitoring Tools: pgAdmin, pgBadger, pg_stat_statements
- Connection Pooling: PgBouncer, pgpool-II