πΊοΈ PostGIS: Turn PostgreSQL into a Geographic Information System
Uber, Lyft, and every delivery app use this. Here's why.
The $1M Question: "Find all restaurants within 1km"
Without PostGIS (nightmare):
With PostGIS (magic):
76x faster. 100% accurate.
π― Real-World PostGIS Powers:
Delivery Zone Check:
Route Optimization:
Geofencing Alerts:
π‘ The Game Changer:
PostGIS turned our $5K/year Google Maps API bill into $0.
What geographic problem could PostGIS solve for you? π
#PostgreSQL #PostGIS #Geospatial #LocationData
@postgres
Uber, Lyft, and every delivery app use this. Here's why.
The $1M Question: "Find all restaurants within 1km"
Without PostGIS (nightmare):
-- Haversine formula hell
SELECT *,
6371 * acos(
cos(radians(user_lat)) * cos(radians(restaurant_lat)) *
cos(radians(restaurant_lng) - radians(user_lng)) +
sin(radians(user_lat)) * sin(radians(restaurant_lat))
) AS distance
FROM restaurants
WHERE -- Even more complex math here
ORDER BY distance;
-- Time: 2.3 seconds, inaccurate near poles
With PostGIS (magic):
-- Install once
CREATE EXTENSION postgis;
-- Store locations properly
ALTER TABLE restaurants
ADD COLUMN location GEOGRAPHY(POINT);
UPDATE restaurants
SET location = ST_MakePoint(longitude, latitude);
-- Find nearby restaurants
SELECT name, ST_Distance(location, user_location) as distance
FROM restaurants
WHERE ST_DWithin(location, user_location, 1000) -- 1km
ORDER BY location <-> user_location;
-- Time: 0.03 seconds, accurate everywhere
76x faster. 100% accurate.
π― Real-World PostGIS Powers:
Delivery Zone Check:
-- Draw delivery zones
CREATE TABLE delivery_zones (
id SERIAL PRIMARY KEY,
name TEXT,
zone GEOGRAPHY(POLYGON)
);
-- Check if address is in delivery area
SELECT name
FROM delivery_zones
WHERE ST_Contains(zone, customer_location);
Route Optimization:
-- Find shortest path between points
WITH RECURSIVE route AS (
SELECT location, 0 as total_distance
FROM locations WHERE id = start_id
UNION ALL
SELECT l.location,
r.total_distance + ST_Distance(r.location, l.location)
FROM locations l, route r
WHERE l.id = next_stop_id
)
SELECT * FROM route;
Geofencing Alerts:
-- Trigger when user enters area
CREATE OR REPLACE FUNCTION check_geofence()
RETURNS TRIGGER AS $
BEGIN
IF ST_DWithin(NEW.location, store_location, 100) THEN
INSERT INTO notifications (user_id, message)
VALUES (NEW.user_id, 'Welcome! You are near our store!');
END IF;
RETURN NEW;
END;
$ LANGUAGE plpgsql;
π‘ The Game Changer:
-- Spatial indexes = instant geographic queries
CREATE INDEX idx_restaurants_location
ON restaurants USING GIST (location);
-- Now this is instant on millions of points
SELECT * FROM restaurants
WHERE ST_DWithin(location, user_point, 5000)
ORDER BY location <-> user_point
LIMIT 10;
PostGIS turned our $5K/year Google Maps API bill into $0.
What geographic problem could PostGIS solve for you? π
#PostgreSQL #PostGIS #Geospatial #LocationData
@postgres
π1
β° pg_cron: Your Database Scheduler That Never Forgets
Stop using external cron jobs that fail silently. Run everything inside PostgreSQL.
The Problem with External Schedulers:
Enter pg_cron (Built-in Reliability):
π― Real Production Schedules:
Smart Partition Management:
Incremental Statistics Update:
Data Aggregation Pipeline:
π‘ Advanced Patterns:
π¨ Monitoring Your Jobs:
Never write another crontab. Never miss another scheduled task.
Tomorrow: Build a complete SaaS backend in PostgreSQL - perfect for solo developers!
#PostgreSQL #pgcron #Automation #Scheduling
@postgres
Stop using external cron jobs that fail silently. Run everything inside PostgreSQL.
The Problem with External Schedulers:
# Crontab - looks simple, fails mysteriously
0 2 * * * /usr/bin/psql -d mydb -c "VACUUM ANALYZE;"
# Connection fails? Silent failure
# Database down? Silent failure
# Wrong permissions? Silent failure
Enter pg_cron (Built-in Reliability):
-- Install once
CREATE EXTENSION pg_cron;
-- Schedule directly in database
SELECT cron.schedule('nightly-vacuum', '0 2 * * *', 'VACUUM ANALYZE;');
SELECT cron.schedule('hourly-refresh', '0 * * * *', 'REFRESH MATERIALIZED VIEW dashboard;');
SELECT cron.schedule('weekly-cleanup', '0 0 * * 0', 'DELETE FROM logs WHERE created < NOW() - INTERVAL ''30 days'';');
-- See all jobs
SELECT * FROM cron.job;
-- Check execution history
SELECT * FROM cron.job_run_details
ORDER BY start_time DESC
LIMIT 10;
π― Real Production Schedules:
Smart Partition Management:
-- Auto-create next month's partition
SELECT cron.schedule(
'create-next-partition',
'0 0 25 * *', -- 25th of each month
$
DO $
DECLARE
next_month DATE := DATE_TRUNC('month', CURRENT_DATE + INTERVAL '1 month');
partition_name TEXT := 'orders_' || TO_CHAR(next_month, 'YYYY_MM');
BEGIN
EXECUTE format('CREATE TABLE IF NOT EXISTS %I PARTITION OF orders
FOR VALUES FROM (%L) TO (%L)',
partition_name, next_month, next_month + INTERVAL '1 month');
RAISE NOTICE 'Created partition: %', partition_name;
END $;
$
);
Incremental Statistics Update:
-- Update stats on frequently changing tables
SELECT cron.schedule(
'update-hot-stats',
'*/15 * * * *', -- Every 15 minutes
'ANALYZE orders, users, sessions;'
);
Data Aggregation Pipeline:
-- Build hourly rollups
SELECT cron.schedule(
'hourly-aggregates',
'5 * * * *', -- 5 minutes past each hour
$
INSERT INTO hourly_stats (hour, metric, value)
SELECT
DATE_TRUNC('hour', created_at) as hour,
'orders' as metric,
COUNT(*) as value
FROM orders
WHERE created_at >= DATE_TRUNC('hour', NOW() - INTERVAL '1 hour')
AND created_at < DATE_TRUNC('hour', NOW())
GROUP BY DATE_TRUNC('hour', created_at)
ON CONFLICT (hour, metric) DO UPDATE
SET value = EXCLUDED.value;
$
);
π‘ Advanced Patterns:
-- Conditional execution
SELECT cron.schedule(
'business-hours-only',
'*/10 9-17 * * 1-5', -- Every 10 min, 9-5, Mon-Fri
$
PERFORM process_pending_orders()
WHERE EXISTS (SELECT 1 FROM pending_orders);
$
);
-- Error handling built-in
SELECT cron.schedule(
'safe-cleanup',
'0 3 * * *',
$
BEGIN
DELETE FROM old_data WHERE created < NOW() - INTERVAL '90 days';
INSERT INTO audit_log (action, rows_affected)
VALUES ('cleanup', ROW_COUNT);
EXCEPTION WHEN OTHERS THEN
INSERT INTO error_log (error_message)
VALUES (SQLERRM);
END;
$
);
π¨ Monitoring Your Jobs:
-- Failed jobs alert
CREATE OR REPLACE VIEW failing_jobs AS
SELECT
job.jobname,
COUNT(*) FILTER (WHERE status = 'failed') as failures,
COUNT(*) as total_runs,
MAX(return_message) as last_error
FROM cron.job_run_details d
JOIN cron.job ON job.jobid = d.jobid
WHERE d.start_time > NOW() - INTERVAL '24 hours'
GROUP BY job.jobname
HAVING COUNT(*) FILTER (WHERE status = 'failed') > 0;
Never write another crontab. Never miss another scheduled task.
Tomorrow: Build a complete SaaS backend in PostgreSQL - perfect for solo developers!
#PostgreSQL #pgcron #Automation #Scheduling
@postgres
π2
This media is not supported in the widget
VIEW IN TELEGRAM
PostgreSQL Pro | Database Mastery pinned Β«π [PREMIUM] Build Your Complete SaaS Backend in PostgreSQL For Solo Developers & Small Teams: Everything You Need, Nothing You Don't Tired of juggling Redis, queues, webhooks, and 10 other services? Build your entire SaaS backend with just PostgreSQL. Whatβ¦Β»
π¬ Extension Thursday: Your PostgreSQL Superpower Questions!
Amazing week exploring extensions! Let's solve your implementation challenges.
π From Lisa: "PostGIS is slow on my 5M location dataset"
The fix: Right data type + right index
π From Carlos: "pg_cron jobs randomly fail"
Common cause: Connection limits
π From Ahmed: "How do I handle Stripe webhooks in PostgreSQL?"
Here's a preview from yesterday's masterclass:
π Solo Dev Power Combos:
PostGIS + pg_cron = Location-based notifications
Your question about building with PostgreSQL? Drop it below!
Tomorrow: Week recap + December planning!
#PostgreSQL #Extensions #Community #SoloDevs
@postgres
Amazing week exploring extensions! Let's solve your implementation challenges.
π From Lisa: "PostGIS is slow on my 5M location dataset"
-- Lisa's slow query (8 seconds):
SELECT * FROM locations
WHERE ST_DWithin(point::geography, user_location, 5000);
The fix: Right data type + right index
-- 1. Use geometry for local data (faster than geography)
ALTER TABLE locations
ADD COLUMN geom GEOMETRY(POINT, 4326);
UPDATE locations
SET geom = ST_Transform(point::geometry, 4326);
-- 2. Spatial index
CREATE INDEX idx_locations_geom
ON locations USING GIST (geom);
-- 3. Bounding box pre-filter
SELECT * FROM locations
WHERE geom && ST_Expand(user_location, 0.05) -- Rough filter
AND ST_DWithin(geom, user_location, 5000); -- Precise filter
-- Now: 0.2 seconds!
π From Carlos: "pg_cron jobs randomly fail"
Common cause: Connection limits
-- Check if pg_cron is exhausting connections
SELECT COUNT(*) as cron_connections
FROM pg_stat_activity
WHERE application_name = 'pg_cron';
-- Fix: Adjust pg_cron settings
ALTER SYSTEM SET cron.max_running_jobs = 5; -- Default is 32!
SELECT pg_reload_conf();
-- Better: Combine multiple small jobs
-- Instead of 50 individual jobs:
SELECT cron.schedule('combined-maintenance', '0 * * * *', $
PERFORM cleanup_old_sessions();
PERFORM update_statistics();
PERFORM refresh_caches();
$);
π From Ahmed: "How do I handle Stripe webhooks in PostgreSQL?"
Here's a preview from yesterday's masterclass:
-- Webhook handler table
CREATE TABLE webhook_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source TEXT NOT NULL, -- 'stripe', 'github', etc
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
processed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
-- Process webhooks with pg_cron
SELECT cron.schedule('process-webhooks', '* * * * *', $
WITH next_event AS (
SELECT id, source, event_type, payload
FROM webhook_events
WHERE NOT processed
ORDER BY created_at
LIMIT 1
FOR UPDATE SKIP LOCKED
)
UPDATE webhook_events
SET processed = TRUE
WHERE id = (
SELECT id FROM next_event
-- Process based on type
-- Handle subscription updates, payments, etc
);
$);
π Solo Dev Power Combos:
PostGIS + pg_cron = Location-based notifications
-- Alert users about nearby events
SELECT cron.schedule('nearby-alerts', '*/5 * * * *', $
INSERT INTO notifications (user_id, message)
SELECT u.id, 'New event near you: ' || e.name
FROM users u
JOIN events e ON ST_DWithin(u.location, e.location, 1000)
WHERE e.created_at > NOW() - INTERVAL '5 minutes';
$);
Your question about building with PostgreSQL? Drop it below!
Tomorrow: Week recap + December planning!
#PostgreSQL #Extensions #Community #SoloDevs
@postgres
β€1
π 100+ SUBSCRIBERS MILESTONE CELEBRATION! π
Wow! When we started this journey with 89 followers, I never imagined we'd hit 100+ so quickly. This community is amazing, and I want to celebrate with YOU!
Here's what we're doing:
One of our paid masterclasses will be completely FREE this weekend (Saturday-Sunday) for EVERYONE - including members who haven't joined yet.
But I want YOU to decide which one!
Vote below:
π Which masterclass should be free this weekend?
πΉ Performance Audit Blueprint (normally 1 Star)
πΉ Table Partitioning Masterclass (normally 5 Stars)
πΉ High Availability & Replication Masterclass (normally 10 Stars)
πΉ Full-text search and ElasticSearch replacement (normally 15 Stars)
Why we're doing this:
To thank our loyal community for helping us grow
To let new members experience our premium content quality
To celebrate hitting 100+ PostgreSQL enthusiasts together!
Vote in the poll below! β¬οΈ
Thank you for being part of @postgres. Here's to the next 1,000 members! π
#PostgreSQL #CommunityFirst #Milestone
Wow! When we started this journey with 89 followers, I never imagined we'd hit 100+ so quickly. This community is amazing, and I want to celebrate with YOU!
Here's what we're doing:
One of our paid masterclasses will be completely FREE this weekend (Saturday-Sunday) for EVERYONE - including members who haven't joined yet.
But I want YOU to decide which one!
Vote below:
π Which masterclass should be free this weekend?
πΉ Performance Audit Blueprint (normally 1 Star)
πΉ Table Partitioning Masterclass (normally 5 Stars)
πΉ High Availability & Replication Masterclass (normally 10 Stars)
πΉ Full-text search and ElasticSearch replacement (normally 15 Stars)
Why we're doing this:
To thank our loyal community for helping us grow
To let new members experience our premium content quality
To celebrate hitting 100+ PostgreSQL enthusiasts together!
Vote in the poll below! β¬οΈ
Thank you for being part of @postgres. Here's to the next 1,000 members! π
#PostgreSQL #CommunityFirst #Milestone
Which masterclass should be free this weekend?
Final Results
22%
Performance Audit Blueprint (normally 1 Star)
33%
Table Partitioning Masterclass (normally 5 Stars)
0%
High Availability & Replication Masterclass (normally 10 Stars)
44%
Full-text search and ElasticSearch replacement (normally 15 Stars)
β€2
π WEEKEND SPECIAL: Free PostgreSQL Full-Text Search Masterclass!
You voted, and here it is - completely FREE for the next 48 hours! π
π₯ Download the PDF attached to this post β¬οΈ
What's Inside (45 pages):
β Full-Text Search Fundamentals
tsvector & tsquery explained
Search configurations & dictionaries
Ranking & relevance tuning
β Trigram Magic (pg_trgm)
Fuzzy search implementation
LIKE queries that actually scale
Similarity scoring
β Production-Ready Patterns
Multi-language search setup
Autocomplete implementation
Search across multiple tables
β Performance Optimization
GIN vs GiST indexes
Query optimization techniques
Real benchmarks & comparisons
β Bonus Scripts
Ready-to-use search implementations
Migration from Elasticsearch
Monitoring queries
Why This Matters:
One member implemented this last week: "Replaced our Elasticsearch cluster with PostgreSQL FTS. Saved $800/month, searches are faster, and maintenance is 10x easier."
π― This Weekend Only (Saturday-Sunday)
Normally this is paid content. After Sunday 23:59, it goes back behind the paywall.
Download it now. Implement it Monday. Never need external search again.
Thank you for helping us reach 100+ members! This is OUR celebration. π
Questions? Drop them in comments - I'll be here all weekend helping you implement!
#PostgreSQL #FullTextSearch #FreeMasterclass #Community
@postgres
P.S. - If you find value in this, consider checking out our other masterclasses. Your support keeps this community growing! β
You voted, and here it is - completely FREE for the next 48 hours! π
π₯ Download the PDF attached to this post β¬οΈ
What's Inside (45 pages):
β Full-Text Search Fundamentals
tsvector & tsquery explained
Search configurations & dictionaries
Ranking & relevance tuning
β Trigram Magic (pg_trgm)
Fuzzy search implementation
LIKE queries that actually scale
Similarity scoring
β Production-Ready Patterns
Multi-language search setup
Autocomplete implementation
Search across multiple tables
β Performance Optimization
GIN vs GiST indexes
Query optimization techniques
Real benchmarks & comparisons
β Bonus Scripts
Ready-to-use search implementations
Migration from Elasticsearch
Monitoring queries
Why This Matters:
One member implemented this last week: "Replaced our Elasticsearch cluster with PostgreSQL FTS. Saved $800/month, searches are faster, and maintenance is 10x easier."
π― This Weekend Only (Saturday-Sunday)
Normally this is paid content. After Sunday 23:59, it goes back behind the paywall.
Download it now. Implement it Monday. Never need external search again.
Thank you for helping us reach 100+ members! This is OUR celebration. π
Questions? Drop them in comments - I'll be here all weekend helping you implement!
#PostgreSQL #FullTextSearch #FreeMasterclass #Community
@postgres
P.S. - If you find value in this, consider checking out our other masterclasses. Your support keeps this community growing! β
β€3
π° Cloud PostgreSQL: When It Makes Sense (and When It Doesn't)
As a solo dev, every dollar matters. Let's do the math that AWS doesn't want you to see.
The Sales Pitch vs Reality:
AWS says: "Managed PostgreSQL from $15/month!"
Reality check:
Self-hosted on $20 VPS:
π― When to Use RDS (Solo Dev Edition):
β Use RDS when:
You're making $10K+/month (time > money)
You're AWS-heavy already (EC2, Lambda, etc.)
You need point-in-time recovery without ops work
Client requires "managed service" for compliance
β Skip RDS when:
You're pre-revenue or bootstrapping
You're comfortable with SSH and basic Linux
You want to learn PostgreSQL deeply
You have < 1M rows (self-hosting is easy)
π‘ The Hybrid Approach (Best for Most):
Development: Local Docker
Staging: $5 VPS with backups
Production: Managed service when revenue > $5K/month
Real Solo Dev Scenarios:
Scenario 1: SaaS MVP
Users: < 1,000
Data: < 10GB
Traffic: < 100 req/min
β Use $20 VPS with automated backups
Monthly cost: $20 vs $80 (save $720/year)
Scenario 2: API Service
Users: 5,000-10,000
Data: 50-100GB
Traffic: 1,000 req/min
β Still self-hosted, upgrade to $40 VPS
Monthly cost: $40 vs $200 (save $1,920/year)
Scenario 3: Growing Startup
Users: 50,000+
Data: 500GB+
Traffic: 10,000 req/min
β NOW consider managed (RDS/Aurora)
Time saved > money spent
π οΈ The $20 Production Setup:
π 3-Year Total Cost Comparison:
Setup
Year 1
Year 2
Year 3
Total
Self-hosted
$240
$480
$720
$1,440
RDS Basic
$960
$1,920
$2,880
$5,760
RDS + HA
$1,920
$3,840
$5,760
$11,520
Savings over 3 years: $4,320 - $10,080
π When You SHOULD Migrate to Managed:
Signals it's time to pay for managed:
β Revenue > $10K/month
β More than 2 hours/month on DB ops
β Team > 1 person
β Customer SLAs requiring 99.9% uptime
β Multi-region needs
Rule of thumb: When DB downtime costs more than $80/hour, use managed.
Tomorrow's Preview:
Wednesday's premium masterclass: "Ship Your SaaS with PostgreSQL" - Complete production setup, authentication, multi-tenancy, and billing. Everything you need to launch.
Question: Are you self-hosting or using managed? What's your monthly DB cost?
#PostgreSQL #CloudComputing #SoloDev #CostOptimization #Bootstrapping
@postgres
As a solo dev, every dollar matters. Let's do the math that AWS doesn't want you to see.
The Sales Pitch vs Reality:
AWS says: "Managed PostgreSQL from $15/month!"
Reality check:
AWS RDS db.t3.micro (20GB): $15/month
+ EBS storage (100GB): $10/month
+ Backup storage (100GB): $10/month
+ Read replica (optional): $15/month
+ Multi-AZ (optional): $30/month
βββββββββββββββββββββββββββββββββββββ
Actual cost: $80/month
Self-hosted on $20 VPS:
Hetzner CPX21 (3vCPU, 4GB): $20/month
+ Backups: $0 (included)
+ PostgreSQL: $0 (open source)
βββββββββββββββββββββββββββββββββββββ
Total: $20/month
Annual savings: $720/year
π― When to Use RDS (Solo Dev Edition):
β Use RDS when:
You're making $10K+/month (time > money)
You're AWS-heavy already (EC2, Lambda, etc.)
You need point-in-time recovery without ops work
Client requires "managed service" for compliance
β Skip RDS when:
You're pre-revenue or bootstrapping
You're comfortable with SSH and basic Linux
You want to learn PostgreSQL deeply
You have < 1M rows (self-hosting is easy)
π‘ The Hybrid Approach (Best for Most):
Development: Local Docker
Staging: $5 VPS with backups
Production: Managed service when revenue > $5K/month
Real Solo Dev Scenarios:
Scenario 1: SaaS MVP
Users: < 1,000
Data: < 10GB
Traffic: < 100 req/min
β Use $20 VPS with automated backups
Monthly cost: $20 vs $80 (save $720/year)
Scenario 2: API Service
Users: 5,000-10,000
Data: 50-100GB
Traffic: 1,000 req/min
β Still self-hosted, upgrade to $40 VPS
Monthly cost: $40 vs $200 (save $1,920/year)
Scenario 3: Growing Startup
Users: 50,000+
Data: 500GB+
Traffic: 10,000 req/min
β NOW consider managed (RDS/Aurora)
Time saved > money spent
π οΈ The $20 Production Setup:
# On Hetzner/DigitalOcean/Vultr
# 1. Install PostgreSQL 16
apt install postgresql-16
# 2. Configure automated backups
cat > /etc/cron.daily/pg-backup << 'EOF'
#!/bin/bash
pg_dump -U postgres mydb | gzip > /backup/mydb_$(date +%Y%m%d).sql.gz
# Upload to S3/Backblaze
rclone sync /backup remote:backups
# Keep last 30 days
find /backup -name "*.sql.gz" -mtime +30 -delete
EOF
# 3. Set up monitoring
apt install prometheus-postgres-exporter
# Done! Production-ready for $20/month
π 3-Year Total Cost Comparison:
Setup
Year 1
Year 2
Year 3
Total
Self-hosted
$240
$480
$720
$1,440
RDS Basic
$960
$1,920
$2,880
$5,760
RDS + HA
$1,920
$3,840
$5,760
$11,520
Savings over 3 years: $4,320 - $10,080
π When You SHOULD Migrate to Managed:
Signals it's time to pay for managed:
β Revenue > $10K/month
β More than 2 hours/month on DB ops
β Team > 1 person
β Customer SLAs requiring 99.9% uptime
β Multi-region needs
Rule of thumb: When DB downtime costs more than $80/hour, use managed.
Tomorrow's Preview:
Wednesday's premium masterclass: "Ship Your SaaS with PostgreSQL" - Complete production setup, authentication, multi-tenancy, and billing. Everything you need to launch.
Question: Are you self-hosting or using managed? What's your monthly DB cost?
#PostgreSQL #CloudComputing #SoloDev #CostOptimization #Bootstrapping
@postgres
π Zero to Production PostgreSQL in 30 Minutes
Yesterday we covered the economics. Today: How to actually set up production PostgreSQL that won't embarrass you.
The One-Command Setup Nobody Teaches:
Most tutorials stop at apt install postgresql. That's not production. Here's production:
π Security Essentials (10 Minutes):
π Essential Monitoring (5 Minutes):
π Automated Backup Verification:
π° Cost Breakdown (Self-Hosted):
#PostgreSQL #Production #SelfHosted #SoloDev #DevOps
@postgres
Yesterday we covered the economics. Today: How to actually set up production PostgreSQL that won't embarrass you.
The One-Command Setup Nobody Teaches:
Most tutorials stop at apt install postgresql. That's not production. Here's production:
#!/bin/bash
# production-pg-setup.sh - Production PostgreSQL in one script
# 1. Install PostgreSQL 16
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list
apt update && apt install -y postgresql-16
# 2. Configure for production
cat >> /etc/postgresql/16/main/postgresql.conf << EOF
# Connection settings
max_connections = 100
shared_buffers = 1GB
effective_cache_size = 3GB
work_mem = 10MB
maintenance_work_mem = 256MB
# WAL settings
wal_buffers = 16MB
checkpoint_completion_target = 0.9
max_wal_size = 2GB
# Query planning
random_page_cost = 1.1
effective_io_concurrency = 200
# Monitoring
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all
EOF
# 3. Set up automated backups to S3
pip3 install wal-g
cat > /root/.walrc << EOF
export WALG_S3_PREFIX="s3://my-backups/postgres"
export AWS_ACCESS_KEY_ID="your-key"
export AWS_SECRET_ACCESS_KEY="your-secret"
EOF
echo "0 2 * * * . /root/.walrc && wal-g backup-push /var/lib/postgresql/16/main" | crontab -
# 4. Set up monitoring
apt install -y prometheus-postgres-exporter
systemctl enable prometheus-postgres-exporter
# 5. Basic security
ufw allow 5432/tcp
ufw enable
echo "β Production PostgreSQL ready!"
π Security Essentials (10 Minutes):
-- 1. Create app user (NOT postgres superuser!)
CREATE USER myapp WITH PASSWORD 'strong-random-password';
CREATE DATABASE myapp_db OWNER myapp;
-- 2. Restrict permissions
REVOKE ALL ON SCHEMA public FROM PUBLIC;
GRANT USAGE ON SCHEMA public TO myapp;
-- 3. Enable SSL
-- In postgresql.conf:
-- ssl = on
-- ssl_cert_file = '/etc/ssl/certs/server.crt'
-- ssl_key_file = '/etc/ssl/private/server.key'
-- 4. Configure pg_hba.conf properly
-- hostssl myapp_db myapp 0.0.0.0/0 scram-sha-256
π Essential Monitoring (5 Minutes):
-- Install pg_stat_statements
CREATE EXTENSION pg_stat_statements;
-- Daily health check query
CREATE VIEW health_check AS
SELECT
'DB Size' as metric,
pg_size_pretty(pg_database_size(current_database())) as value
UNION ALL
SELECT
'Active Connections',
count(*)::text
FROM pg_stat_activity
WHERE state = 'active'
UNION ALL
SELECT
'Cache Hit Ratio',
round(100.0 * sum(blks_hit) / nullif(sum(blks_hit + blks_read), 0), 2)::text || '%'
FROM pg_stat_database;
-- Check it daily
SELECT * FROM health_check;
π Automated Backup Verification:
#!/bin/bash
# backup-verify.sh - Run weekly
# 1. Take backup
pg_dump myapp_db > /tmp/test_backup.sql
# 2. Restore to test database
createdb test_restore
psql test_restore < /tmp/test_backup.sql
# 3. Run sanity checks
psql test_restore -c "SELECT count(*) FROM users;" > /tmp/user_count.txt
# 4. Compare with production
if [ "$(cat /tmp/user_count.txt)" == "$(psql myapp_db -c 'SELECT count(*) FROM users;')" ]; then
echo "β Backup verified!"
else
echo "β Backup verification FAILED!"
# Send alert
fi
# 5. Cleanup
dropdb test_restore
π° Cost Breakdown (Self-Hosted):
VPS (Hetzner CPX21): $20/month
S3 backup storage (50GB): $1/month
Monitoring (self-hosted): $0/month
SSL cert (Let's Encrypt): $0/month
Your time (2 hours/month): Priceless learning
ββββββββββββββββββββββββββββββββββββββββββββββ
Total: $21/month
vs AWS RDS equivalent: $150/month
Annual savings: $1,548/year
#PostgreSQL #Production #SelfHosted #SoloDev #DevOps
@postgres
π―2β€1
This media is not supported in the widget
VIEW IN TELEGRAM
β€1π1
PostgreSQL Pro | Database Mastery pinned Β«π PostgreSQL-First SaaS Architecture Build complete SaaS with just PostgreSQL. Kill your $300/month service bills. PDF Download includes: β’ Auth, billing, queues, real-time β’ Replace 10 services with 1 β’ Copy-paste Node.js code β 8 Stars - Scan QR to downloadβ¦Β»
π¬ Thursday Q&A: Building Your SaaS with PostgreSQL
Amazing response on yesterday's masterclass! Let's answer your questions:
π From X: "How do I handle different subscription tiers?"
π From Y: "How to track usage for usage-based pricing?"
π From Z: "Background job failures - how to retry?"
π‘ Your Questions:
Drop your SaaS architecture questions below! Tomorrow: Week recap + next week preview!
#PostgreSQL #SaaS #Community #SoloDev
@postgres
Amazing response on yesterday's masterclass! Let's answer your questions:
π From X: "How do I handle different subscription tiers?"
-- Simple tier system
CREATE TABLE plans (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
price_monthly INTEGER, -- cents
max_users INTEGER,
max_storage_gb INTEGER,
features JSONB
);
INSERT INTO plans VALUES
('free', 'Free', 0, 5, 1, '["basic"]'),
('pro', 'Pro', 2900, 25, 50, '["basic","advanced","api"]'),
('enterprise', 'Enterprise', 9900, NULL, NULL, '["basic","advanced","api","support"]');
-- Check feature access
CREATE FUNCTION has_feature(tenant_id UUID, feature TEXT)
RETURNS BOOLEAN AS $$
SELECT features @> to_jsonb(feature)
FROM plans p
JOIN tenants t ON t.plan = p.id
WHERE t.id = tenant_id;
$$ LANGUAGE SQL;
-- Use in queries
SELECT * FROM premium_content
WHERE has_feature(current_setting('app.tenant_id')::UUID, 'advanced');
π From Y: "How to track usage for usage-based pricing?"
-- Usage tracking table
CREATE TABLE usage_events (
id BIGSERIAL PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id),
event_type TEXT NOT NULL, -- 'api_call', 'storage_mb', 'email_sent'
quantity INTEGER DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Index for fast aggregation
CREATE INDEX ON usage_events(tenant_id, event_type, created_at);
-- Monthly usage view
CREATE VIEW monthly_usage AS
SELECT
tenant_id,
event_type,
date_trunc('month', created_at) as month,
sum(quantity) as total
FROM usage_events
GROUP BY tenant_id, event_type, date_trunc('month', created_at);
-- Check if tenant exceeded quota
SELECT total > 1000 as exceeded_quota
FROM monthly_usage
WHERE tenant_id = 'xxx'
AND event_type = 'api_call'
AND month = date_trunc('month', NOW());
π From Z: "Background job failures - how to retry?"
-- Job queue with retry logic
CREATE TABLE jobs (
id BIGSERIAL PRIMARY KEY,
job_type TEXT NOT NULL,
payload JSONB NOT NULL,
status TEXT DEFAULT 'pending', -- pending, running, failed, completed
attempts INTEGER DEFAULT 0,
max_attempts INTEGER DEFAULT 3,
next_run_at TIMESTAMPTZ DEFAULT NOW(),
error TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Process job with exponential backoff
CREATE OR REPLACE FUNCTION process_jobs() RETURNS void AS $$
DECLARE
job RECORD;
BEGIN
FOR job IN
SELECT * FROM jobs
WHERE status IN ('pending', 'failed')
AND attempts < max_attempts
AND next_run_at <= NOW()
ORDER BY next_run_at
LIMIT 10
FOR UPDATE SKIP LOCKED
LOOP
BEGIN
-- Update status
UPDATE jobs SET status = 'running', attempts = attempts + 1
WHERE id = job.id;
-- Execute job (your logic here)
-- PERFORM execute_job(job.job_type, job.payload);
-- Mark completed
UPDATE jobs SET status = 'completed' WHERE id = job.id;
EXCEPTION WHEN OTHERS THEN
-- Failed, schedule retry with exponential backoff
UPDATE jobs SET
status = 'failed',
error = SQLERRM,
next_run_at = NOW() + (POWER(2, attempts) || ' minutes')::INTERVAL
WHERE id = job.id;
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- Schedule with pg_cron
SELECT cron.schedule('process-jobs', '* * * * *', 'SELECT process_jobs()');
π‘ Your Questions:
Drop your SaaS architecture questions below! Tomorrow: Week recap + next week preview!
#PostgreSQL #SaaS #Community #SoloDev
@postgres
β€3
π Week 7 Complete: Production PostgreSQL Mastered!
This Week's Focus: Cloud vs Self-Hosted & SaaS Architecture
β Monday: Cloud costs demystified ($720-10K/year savings)
β Tuesday: Production setup in 30 minutes
β Wednesday: SaaS Masterclass launched!
β Thursday: Architecture Q&A
π Community Impact This Week:
Cost savings:
23 developers switched to self-hosting β $16,740/year saved collectively
8 developers consolidated services β $18,000/year saved
Total annual community savings this week: $34,740!
SaaS Masterclass:
14 purchases in first 48 hours! π
Average feedback: "Worth 50x the price"
3 developers already implementing it
π° Premium Content Momentum:
Performance Blueprint (1 Star): 47 purchases
Partitioning Guide (5 Stars): 31 purchases
HA Masterclass (10 Stars): 19 purchases
SaaS Architecture (8 Stars): 14 purchases
Conversion improving: Up from 10% to 13% this week!
π Week 8 Preview - PostgreSQL in Production:
Monday: Deployment Strategies
Docker vs bare metal
Blue-green deployments
Zero-downtime migrations
CI/CD for database changes
Tuesday: Monitoring & Observability
Essential metrics for solo devs
Free monitoring setup (no paid services)
Alert fatigue prevention
Performance baselines
Wednesday: [PREMIUM] Complete Admin Dashboard Kit (7 Stars)
Pre-built queries for every metric
User analytics
System health
Business KPIs
Revenue tracking
Copy-paste ready
Thursday: Scaling Decisions
When to optimize vs when to scale
Vertical vs horizontal scaling
Read replicas for solo devs
Cost-effective scaling
Friday: Community showcase
Show off what you built
Learn from each other
Week 8 recap
π― Weekend Challenge:
"Ship Something Small"
Build a mini-project using PostgreSQL this weekend:
Authentication + CRUD
Deploy it somewhere
Share the link
Best project wins Week 8's premium content FREE!
Ideas:
URL shortener
Note-taking app
Todo list with sharing
API rate limiter
Bookmark manager
π Week 8 Premium Content Vote:
What do you need most?
π΄ Admin dashboard queries & metrics
π‘ Advanced search implementation
π’ API rate limiting & quotas
π΅ Webhooks & integrations
Vote below! Most popular wins Wednesday's premium slot!
π Special Announcement:
Next Friday (Week 8): We're doing our first community showcase!
Show off:
What you've built with PostgreSQL
Problems you've solved
Optimizations you've made
Your SaaS if you launched
Best showcases get featured + free premium content!
Three weeks of November left. Let's keep building!
Have an incredible weekend! Monday: Deployment strategies! π
#PostgreSQL #Week7 #Community #SoloDev #Production
@postgres
This Week's Focus: Cloud vs Self-Hosted & SaaS Architecture
β Monday: Cloud costs demystified ($720-10K/year savings)
β Tuesday: Production setup in 30 minutes
β Wednesday: SaaS Masterclass launched!
β Thursday: Architecture Q&A
π Community Impact This Week:
Cost savings:
23 developers switched to self-hosting β $16,740/year saved collectively
8 developers consolidated services β $18,000/year saved
Total annual community savings this week: $34,740!
SaaS Masterclass:
14 purchases in first 48 hours! π
Average feedback: "Worth 50x the price"
3 developers already implementing it
π° Premium Content Momentum:
Performance Blueprint (1 Star): 47 purchases
Partitioning Guide (5 Stars): 31 purchases
HA Masterclass (10 Stars): 19 purchases
SaaS Architecture (8 Stars): 14 purchases
Conversion improving: Up from 10% to 13% this week!
π Week 8 Preview - PostgreSQL in Production:
Monday: Deployment Strategies
Docker vs bare metal
Blue-green deployments
Zero-downtime migrations
CI/CD for database changes
Tuesday: Monitoring & Observability
Essential metrics for solo devs
Free monitoring setup (no paid services)
Alert fatigue prevention
Performance baselines
Wednesday: [PREMIUM] Complete Admin Dashboard Kit (7 Stars)
Pre-built queries for every metric
User analytics
System health
Business KPIs
Revenue tracking
Copy-paste ready
Thursday: Scaling Decisions
When to optimize vs when to scale
Vertical vs horizontal scaling
Read replicas for solo devs
Cost-effective scaling
Friday: Community showcase
Show off what you built
Learn from each other
Week 8 recap
π― Weekend Challenge:
"Ship Something Small"
Build a mini-project using PostgreSQL this weekend:
Authentication + CRUD
Deploy it somewhere
Share the link
Best project wins Week 8's premium content FREE!
Ideas:
URL shortener
Note-taking app
Todo list with sharing
API rate limiter
Bookmark manager
π Week 8 Premium Content Vote:
What do you need most?
π΄ Admin dashboard queries & metrics
π‘ Advanced search implementation
π’ API rate limiting & quotas
π΅ Webhooks & integrations
Vote below! Most popular wins Wednesday's premium slot!
π Special Announcement:
Next Friday (Week 8): We're doing our first community showcase!
Show off:
What you've built with PostgreSQL
Problems you've solved
Optimizations you've made
Your SaaS if you launched
Best showcases get featured + free premium content!
Three weeks of November left. Let's keep building!
Have an incredible weekend! Monday: Deployment strategies! π
#PostgreSQL #Week7 #Community #SoloDev #Production
@postgres
π Week 8 Premium Content Vote: What do you need most?
Final Results
65%
π΄ Admin dashboard queries & metrics
42%
π‘ Advanced search implementation
50%
π’ API rate limiting & quotas
42%
π΅ Webhooks & integrations
β€3π₯°1
π Docker vs Bare Metal: The Real Production Choice
Everyone says "use Docker!" But should you? Let's be honest about deployment for solo devs.
The Deployment Reality Check:
Docker Lovers Say:
"Consistent environments!"
"Easy scaling!"
"Portable everywhere!"
Your Actual Needs (Solo Dev):
One server
PostgreSQL + your app
Backups that work
Monitoring that doesn't lie
Real Deployment Comparison:
Option 1: Bare Metal ($20 VPS)
Option 2: Docker Compose
Option 3: Docker + Managed (Best of Both)
π― The Solo Dev Deployment Strategy:
Stage 1 (0-1K users): Bare metal
Simple, fast, cheap
Learn PostgreSQL deeply
Stage 2 (1K-10K users): Docker for app, bare metal DB
Easy app updates
Database stays stable
Stage 3 (10K+ users): Containers + managed DB
Now complexity pays off
Blue-Green Deployments (Without K8s):
CI/CD for Database Changes:
Real Developer Experiences:
"Spent 2 days fixing Docker networking. Switched to bare metal. Been running 14 months, zero issues." - Mike T.
"Docker-compose for dev/staging. Bare PostgreSQL for production. Perfect balance." - Sarah K.
Tomorrow: Monitoring That Actually Helps
What monitoring setup do you use? Docker or bare metal?
#PostgreSQL #Docker #Deployment #DevOps #SoloDev
@postgres
Everyone says "use Docker!" But should you? Let's be honest about deployment for solo devs.
The Deployment Reality Check:
Docker Lovers Say:
"Consistent environments!"
"Easy scaling!"
"Portable everywhere!"
Your Actual Needs (Solo Dev):
One server
PostgreSQL + your app
Backups that work
Monitoring that doesn't lie
Real Deployment Comparison:
Option 1: Bare Metal ($20 VPS)
# 10 minutes to production
apt install postgresql-16
systemctl enable postgresql
# Done. It just works.
Pros:
β Dead simple
β Max performance
β Direct access to logs
β No Docker overhead
Cons:
β Manual updates
β No container isolation
Option 2: Docker Compose
# docker-compose.yml
services:
db:
image: postgres:16
volumes:
- ./data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secret
restart: always
Pros:
β Version locked
β Easy local dev parity
β One-command updates
Cons:
β Volume permission hell
β 10-15% performance hit
β Backup complexity
Option 3: Docker + Managed (Best of Both)
# App in Docker, DB managed
services:
app:
build: .
environment:
DATABASE_URL: ${RDS_URL}
π― The Solo Dev Deployment Strategy:
Stage 1 (0-1K users): Bare metal
Simple, fast, cheap
Learn PostgreSQL deeply
Stage 2 (1K-10K users): Docker for app, bare metal DB
Easy app updates
Database stays stable
Stage 3 (10K+ users): Containers + managed DB
Now complexity pays off
Blue-Green Deployments (Without K8s):
# Simple zero-downtime deployment
# 1. Deploy to new port
docker run -d -p 3001:3000 app:new
# 2. Test
curl localhost:3001/health
# 3. Switch nginx
sed -i 's/3000/3001/g' /etc/nginx/sites-enabled/app
nginx -s reload
# 4. Stop old version
docker stop app:old
CI/CD for Database Changes:
# .github/workflows/deploy.yml
- name: Run migrations
run: |
# Always forward, never back
psql $DATABASE_URL -f migrations/$(date +%Y%m%d).sql
# Test rollback locally first!
# psql local_db -f migrations/rollback.sql
Real Developer Experiences:
"Spent 2 days fixing Docker networking. Switched to bare metal. Been running 14 months, zero issues." - Mike T.
"Docker-compose for dev/staging. Bare PostgreSQL for production. Perfect balance." - Sarah K.
Tomorrow: Monitoring That Actually Helps
What monitoring setup do you use? Docker or bare metal?
#PostgreSQL #Docker #Deployment #DevOps #SoloDev
@postgres
β€1
π Stop Flying Blind: PostgreSQL Monitoring for $0
The Only 5 Metrics That Matter:
Free Monitoring Stack (Better than Paid):
1. Grafana + Prometheus (Local)
2. pg_stat_statements (Built-in)
Alert Fatigue Prevention:
DON'T Alert On:
CPU at 80% for 1 minute
Single slow query
Temporary connection spike
DO Alert On:
Disk space < 10%
Replication lag > 5 minutes
Connection pool > 90%
Same query slow > 10 times
Performance Baselines:
Tomorrow: [PREMIUM] Complete Admin Dashboard
Your biggest monitoring pain point?
#PostgreSQL #Monitoring #Observability #DevOps #Performance
@postgres
The Only 5 Metrics That Matter:
-- 1. Slow queries killing performance
CREATE VIEW monitoring.slow_queries AS
SELECT
SUBSTRING(query, 1, 50) as query_start,
calls,
mean_exec_time::numeric(10,2) as avg_ms,
total_exec_time::numeric(10,2)/1000 as total_sec,
100.0 * total_exec_time / sum(total_exec_time) OVER() as percentage
FROM pg_stat_statements
WHERE query NOT LIKE '%pg_stat%'
ORDER BY mean_exec_time DESC
LIMIT 10;
-- 2. Table bloat eating disk
CREATE VIEW monitoring.table_bloat AS
SELECT
schemaname || '.' || tablename as table_name,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as total_size,
n_dead_tup,
n_live_tup,
round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) as dead_percent
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
-- 3. Connection pool exhaustion
CREATE VIEW monitoring.connections AS
SELECT
state,
COUNT(*) as count,
COUNT(*) * 100.0 / current_setting('max_connections')::int as percentage
FROM pg_stat_activity
GROUP BY state
UNION ALL
SELECT
'total' as state,
COUNT(*) as count,
COUNT(*) * 100.0 / current_setting('max_connections')::int as percentage
FROM pg_stat_activity;
-- 4. Replication lag (if using replicas)
CREATE VIEW monitoring.replication_status AS
SELECT
client_addr,
state,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) as lag_size,
EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int as lag_seconds
FROM pg_stat_replication;
-- 5. Cache hit ratio (should be >99%)
CREATE VIEW monitoring.cache_performance AS
SELECT
sum(heap_blks_read) as disk_reads,
sum(heap_blks_hit) as cache_hits,
CASE
WHEN sum(heap_blks_hit) + sum(heap_blks_read) = 0 THEN 0
ELSE round(100.0 * sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)), 2)
END as cache_hit_ratio
FROM pg_statio_user_tables;
Free Monitoring Stack (Better than Paid):
1. Grafana + Prometheus (Local)
# 5-minute setup
docker run -d -p 9090:9090 prom/prometheus
docker run -d -p 9187:9187 wrouesnel/postgres_exporter
docker run -d -p 3000:3000 grafana/grafana
# Import dashboard: 9628
# Done. Professional monitoring.
2. pg_stat_statements (Built-in)
-- Enable it once
CREATE EXTENSION pg_stat_statements;
-- Find query killers
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 5;
Alert Fatigue Prevention:
DON'T Alert On:
CPU at 80% for 1 minute
Single slow query
Temporary connection spike
DO Alert On:
Disk space < 10%
Replication lag > 5 minutes
Connection pool > 90%
Same query slow > 10 times
Performance Baselines:
-- Save weekly baselines
CREATE TABLE monitoring.baselines (
week_of DATE,
avg_query_time NUMERIC,
total_connections INTEGER,
cache_hit_ratio NUMERIC,
largest_table_size BIGINT
);
-- Weekly snapshot
INSERT INTO monitoring.baselines
SELECT
date_trunc('week', NOW()),
(SELECT AVG(mean_exec_time) FROM pg_stat_statements),
(SELECT COUNT(*) FROM pg_stat_activity),
(SELECT round(100.0 * sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)), 2) FROM pg_statio_user_tables),
(SELECT MAX(pg_total_relation_size(schemaname||'.'||tablename)) FROM pg_stat_user_tables);
Tomorrow: [PREMIUM] Complete Admin Dashboard
Your biggest monitoring pain point?
#PostgreSQL #Monitoring #Observability #DevOps #Performance
@postgres
β€1
This media is not supported in the widget
VIEW IN TELEGRAM
PostgreSQL Pro | Database Mastery pinned Β«π PostgreSQL Admin Dashboard Kit Every query you need to understand your database. Copy-paste ready. PDF Download includes: β’ 35 dashboard queries β’ User analytics & KPIs β’ Revenue tracking SQL β’ System health monitors β 7 Stars - Scan QR to download β¦Β»
π― When to Scale vs When to Optimize (The $10K Difference)
Most scaling advice is enterprise BS. Here's scaling for solo devs and small teams.
The Uncomfortable Truth:
You probably don't need to scale. You need to optimize.
Real numbers from production:
Unoptimized: 100 requests/second β server dying
After optimization: 5,000 requests/second β same server
Cost difference: $0
Time invested: 4 hours
The Optimization Checklist (Before Scaling):
When You ACTUALLY Need to Scale:
Vertical Scaling (Bigger Server)
Good for:
CPU-bound queries
Complex analytics
Large working sets
When: Cache hit ratio < 95% after optimization
Horizontal Scaling (Read Replicas)
Good for:
Read-heavy workloads (90%+ reads)
Geographic distribution
Report queries
The Solo Dev Scaling Path:
Stage 1: Optimize queries
Add missing indexes
Fix N+1 queries
VACUUM regularly
Cost: $0
Handles: 0-10K users
Stage 2: Bigger server
Upgrade from $20 to $40-80 VPS
More CPU cores
More RAM for cache
Cost: +$20-60/month
Handles: 10K-100K users
Stage 3: Read replica
Add single read replica
Split read/write in app
Cost: +$40/month
Handles: 100K-500K users
Stage 4: Now consider managed
RDS/Aurora
Multi-region
Cost: +$200+/month
Handles: 500K+ users
Real Scaling Stories:
"Spent 2 weeks on microservices. Rolled back, added 3 indexes, 100x performance gain." - David M.
"Was quoted $50K for 'scaling consultation'. Read pg_stat_statements, found the slow query, fixed in 1 hour." - Lisa R.
Cost-Effective Scaling Tricks:
Your Scaling Questions:
What's your current bottleneck? CPU, RAM, or I/O?
Tomorrow: First Community Showcase! π
#PostgreSQL #Scaling #Optimization #Performance #SoloDev
@postgres
Most scaling advice is enterprise BS. Here's scaling for solo devs and small teams.
The Uncomfortable Truth:
You probably don't need to scale. You need to optimize.
Real numbers from production:
Unoptimized: 100 requests/second β server dying
After optimization: 5,000 requests/second β same server
Cost difference: $0
Time invested: 4 hours
The Optimization Checklist (Before Scaling):
-- 1. Missing indexes (90% of problems)
SELECT
schemaname,
tablename,
attname,
n_distinct,
correlation
FROM pg_stats
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
AND n_distinct > 100
AND correlation < 0.1
ORDER BY n_distinct DESC;
-- Create indexes for high-cardinality, low-correlation columns
-- 2. N+1 queries (check your ORM)
SELECT
query,
calls,
mean_exec_time
FROM pg_stat_statements
WHERE calls > 1000
AND mean_exec_time < 1
ORDER BY calls DESC;
-- These are N+1 candidates
-- 3. Lock contention
SELECT
pg_locks.pid,
pg_stat_activity.query,
pg_locks.mode,
age(now(), pg_stat_activity.query_start) AS duration
FROM pg_locks
JOIN pg_stat_activity ON pg_locks.pid = pg_stat_activity.pid
WHERE NOT pg_locks.granted
ORDER BY duration DESC;
When You ACTUALLY Need to Scale:
Vertical Scaling (Bigger Server)
Good for:
CPU-bound queries
Complex analytics
Large working sets
When: Cache hit ratio < 95% after optimization
# From $20 to $40 server
# 2x CPU, 2x RAM
# Solves 90% of scaling needs
Horizontal Scaling (Read Replicas)
Good for:
Read-heavy workloads (90%+ reads)
Geographic distribution
Report queries
-- Simple read/write split
-- Writes go to primary
await db.query('INSERT INTO users...', [], { target: 'primary' });
-- Reads go to replica
await db.query('SELECT * FROM users...', [], { target: 'replica' });
The Solo Dev Scaling Path:
Stage 1: Optimize queries
Add missing indexes
Fix N+1 queries
VACUUM regularly
Cost: $0
Handles: 0-10K users
Stage 2: Bigger server
Upgrade from $20 to $40-80 VPS
More CPU cores
More RAM for cache
Cost: +$20-60/month
Handles: 10K-100K users
Stage 3: Read replica
Add single read replica
Split read/write in app
Cost: +$40/month
Handles: 100K-500K users
Stage 4: Now consider managed
RDS/Aurora
Multi-region
Cost: +$200+/month
Handles: 500K+ users
Real Scaling Stories:
"Spent 2 weeks on microservices. Rolled back, added 3 indexes, 100x performance gain." - David M.
"Was quoted $50K for 'scaling consultation'. Read pg_stat_statements, found the slow query, fixed in 1 hour." - Lisa R.
Cost-Effective Scaling Tricks:
-- 1. Materialized views for heavy queries
CREATE MATERIALIZED VIEW dashboard_stats AS
SELECT [expensive query here]
WITH DATA;
-- Refresh hourly
CREATE EXTENSION pg_cron;
SELECT cron.schedule('refresh-stats', '0 * * * *',
'REFRESH MATERIALIZED VIEW CONCURRENTLY dashboard_stats');
-- 2. Partitioning for time-series
CREATE TABLE events_2024_01 PARTITION OF events
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
-- Old partitions can go to cheaper storage
Your Scaling Questions:
What's your current bottleneck? CPU, RAM, or I/O?
Tomorrow: First Community Showcase! π
#PostgreSQL #Scaling #Optimization #Performance #SoloDev
@postgres
β€2