Skip to main content

PostgreSQL

PostgreSQL

PostgreSQL is a powerful, open-source relational database system with strong support for ACID compliance, complex queries, and advanced data types.

Overview

  • Versions: 18, 17.6, 16.10 (default: 18)
  • Default Port: 5432
  • Cluster Support: No (Single node only)
  • Use Cases: Relational data, analytics, ACID compliance
  • Features: Extensions, backups, full-text search

Key Features

  • ACID Compliant: Full support for transactions with atomicity, consistency, isolation, and durability
  • Rich Extensions: PostGIS for geospatial data, pg_trgm for text search, and many more
  • Advanced Data Types: JSON/JSONB, arrays, hstore, and custom types
  • Full-Text Search: Built-in text search capabilities with ranking and stemming
  • Concurrent Access: Multi-version concurrency control (MVCC) for high performance
  • Foreign Data Wrappers: Query external data sources as if they were local tables

Resources

Choose the add-on's resources on the create form:

SettingOptionsDefault
CPU (vCPU)Free entry (e.g., 0.5, 1, 2)0.5
Memory1GB, 2GB, 4GB, 8GB, 16GB1GB
Disk Space1GB, 5GB, 10GB, 20GB, 50GB, 100GB10GB
GPU Count0-8 (0 for CPU-only)0

Creating a PostgreSQL Add-on

  1. Navigate to Add-ons and click Create Add-on
  2. On the Create New Add-on page, select PostgreSQL as the type
  3. Choose a version (18, 17.6, or 16.10)
  4. Configure:
    • Add-on Label (required): descriptive name (e.g., "main-database")
    • Description (optional): purpose and notes
    • Resources: CPU, memory, and disk for your workload
  5. Optionally enable automatic backups:
    • Schedule: Hourly, Daily, Weekly, or Monthly
    • Retention: number of backups to keep (1-30, default 7)
  6. Click Create Add-on

Connection Information

Once the add-on is running, the Connection tab of the add-on details page shows the internal host (for apps), port, database, username, password, and a ready-to-use connection string. The same details are exposed to your apps via STRONGLY_SERVICES.

Connection String Format

postgresql://username:password@host:5432/defaultdb

The default database name is defaultdb. Credentials are auto-generated during add-on creation: the username is a randomly generated string (e.g., user_a1b2c3d4) and the password is a 32-character random secret.

Accessing Connection Details

In STRONGLY_SERVICES, add-ons are grouped by type under services.addons, and each entry is one provisioned instance with connection and auth sections:

{
"id": "addon-abc123defg",
"name": "main-database",
"type": "postgres",
"category": "add-on",
"status": "running",
"version": "18",
"connection": {
"connection_string": "postgresql://user_a1b2c3d4:<password>@<internal-host>:5432/defaultdb",
"uri": "postgresql://user_a1b2c3d4:<password>@<internal-host>:5432/defaultdb",
"host": "<internal-host>",
"port": 5432,
"database": "defaultdb"
},
"auth": {
"method": "username_password",
"credentials": { "username": "user_a1b2c3d4", "password": "<password>" }
},
"limits": { "max_connections": 100, "storage_gb": 10 },
"metadata": { "cpu": "0.5", "memory": "1GB", "disk": "10GB", "backup_enabled": false }
}
import os
import json
import psycopg2

# Parse STRONGLY_SERVICES
services = json.loads(os.environ['STRONGLY_SERVICES'])

# Pick your PostgreSQL add-on by name (the label you gave it)
pg = next(
a for a in services['services']['addons']['postgres']
if a['name'] == 'main-database'
)

# Connect using the connection string
conn = psycopg2.connect(pg['connection']['connection_string'])

# Or connect using individual parameters
conn = psycopg2.connect(
host=pg['connection']['host'],
port=pg['connection']['port'],
database=pg['connection']['database'],
user=pg['auth']['credentials']['username'],
password=pg['auth']['credentials']['password']
)

Common Operations

Creating Tables

CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
metadata JSONB
);

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_metadata ON users USING GIN (metadata);
-- Add a full-text search column
ALTER TABLE articles ADD COLUMN search_vector tsvector;

-- Update the search vector
UPDATE articles SET search_vector =
to_tsvector('english', title || ' ' || content);

-- Create an index
CREATE INDEX idx_articles_search ON articles USING GIN (search_vector);

-- Search
SELECT * FROM articles
WHERE search_vector @@ to_tsquery('english', 'postgresql & database');

JSON Operations

-- Query JSONB data
SELECT * FROM users
WHERE metadata @> '{"premium": true}';

-- Update JSONB field
UPDATE users
SET metadata = metadata || '{"last_login": "2025-01-26"}'
WHERE id = 1;

-- Extract JSONB field
SELECT email, metadata->>'plan' as plan
FROM users;

PostgreSQL supports many extensions. Common ones include:

  • PostGIS: Geospatial data support
  • pg_trgm: Trigram matching for fuzzy text search
  • uuid-ossp: UUID generation
  • hstore: Key-value store within PostgreSQL
  • pg_stat_statements: Query performance tracking

Enabling Extensions

-- Enable an extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";

-- List installed extensions
SELECT * FROM pg_extension;

Backup & Restore

PostgreSQL add-ons use pg_dump for backups, creating complete SQL dumps of your database.

Backup Configuration

  • Tool: pg_dump
  • Format: .sql
  • Includes: Schema, data, indexes, constraints
  • Storage: the platform's S3 backup storage

Manual Backup

  1. Go to the add-on details page
  2. Open the Backup tab and click Create Manual Backup (the add-on must be running)
  3. The Backup tab shows the last backup time when complete

Scheduled Backups

Configure during add-on creation or on the Backup tab:

  • Hourly: For critical data with frequent changes
  • Daily: Recommended for most production workloads
  • Weekly/Monthly: For less frequently changing data
  • Retention: number of backups to keep (3, 7, 14, or 30 on the Backup tab)

Performance Optimization

Connection Pooling

Use connection pooling for better performance:

from psycopg2 import pool

# Create a connection pool
connection_pool = pool.SimpleConnectionPool(
minconn=1,
maxconn=20,
host='host',
database='database',
user='user',
password='password'
)

# Get connection from pool
conn = connection_pool.getconn()
# Use connection
# ...
# Return to pool
connection_pool.putconn(conn)

Indexing Strategy

-- B-tree index (default, good for equality and range queries)
CREATE INDEX idx_users_email ON users(email);

-- GIN index (good for JSONB and full-text search)
CREATE INDEX idx_users_metadata ON users USING GIN (metadata);

-- Partial index (index only subset of rows)
CREATE INDEX idx_active_users ON users(email) WHERE active = true;

-- Analyze index usage
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
ORDER BY idx_scan;

Query Optimization

-- Use EXPLAIN to analyze queries
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com';

-- Use VACUUM to reclaim space
VACUUM ANALYZE users;

-- Monitor slow queries (requires pg_stat_statements extension)
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;

Monitoring

The Metrics tab on the add-on details page shows:

  • CPU Usage: CPU utilization percentage
  • Memory Usage: memory utilization percentage
  • Disk Space: disk utilization percentage
  • Network I/O: current throughput
  • Request Stats: connections per minute and average response time
  • Instance Health: instance count and uptime

Best Practices

  1. Use Indexes Wisely: Index frequently queried columns, but avoid over-indexing
  2. Enable Connection Pooling: Reduce connection overhead
  3. Regular VACUUM: Keep database healthy with regular maintenance
  4. Monitor Query Performance: Use EXPLAIN ANALYZE for slow queries
  5. Use Transactions: Wrap related operations in transactions
  6. Backup Regularly: Enable daily backups for production databases
  7. Use Prepared Statements: Prevent SQL injection and improve performance

Migration Guide

From MySQL to PostgreSQL

Key differences to note:

-- MySQL: AUTO_INCREMENT
-- PostgreSQL: SERIAL or IDENTITY
CREATE TABLE users (
id SERIAL PRIMARY KEY
-- or
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
);

-- MySQL: LIMIT offset, count
-- PostgreSQL: LIMIT count OFFSET offset
SELECT * FROM users LIMIT 10 OFFSET 20;

-- MySQL: CONCAT()
-- PostgreSQL: || operator
SELECT first_name || ' ' || last_name AS full_name FROM users;

-- MySQL: IF()
-- PostgreSQL: CASE WHEN
SELECT CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END FROM users;

Troubleshooting

Connection Issues

# Test connection
psql "postgresql://username:password@host:5432/database"

# Check connection limits
SELECT max_connections FROM pg_settings WHERE name = 'max_connections';

# View current connections
SELECT count(*) FROM pg_stat_activity;

Performance Issues

-- Find slow queries
SELECT pid, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC;

-- Check table bloat
SELECT schemaname, tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;

Disk Space Issues

-- Check database size
SELECT pg_size_pretty(pg_database_size(current_database()));

-- Check table sizes
SELECT tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 10;

Support

For issues or questions:

  • Check add-on logs in the Logs tab of the add-on details page
  • Review PostgreSQL official documentation
  • Contact Strongly support through the platform