PostgreSQL Pro | Database Mastery
1.32K subscribers
1 photo
28 links
🐘 PostgreSQL Mastery Hub

🎯 What you get:
- Daily optimization tips
- Performance guides
- Real-world solutions
- Query debugging help
- Production best practices

πŸ“ˆ Join 500+ developers improving their PostgreSQL skills
Download 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"
-- 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
🎁 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! ⭐
❀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:

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:

#!/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?"
-- 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
πŸš€ 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)

# 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:
-- 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):
-- 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
πŸŽ‰ Community Showcase: What YOU Built This Week!

Featured Projects from Our Community:
πŸ† Winner: Task Tracker SaaS by @maria_dev


Built with Week 7's SaaS Architecture
PostgreSQL-only (no Redis, no queues)
Launched in 5 days
Already has 12 paying users!
Stack: Next.js + PostgreSQL on $20 VPS

"The SaaS masterclass saved me weeks. Authentication, billing, everything just works."

πŸ₯ˆ Runner-up: Analytics Dashboard by @alexcoder


Used Week 8's Admin Dashboard queries
Replaced $500/month Mixpanel
50ms query response on 10M events
Materialized views for instant loading

πŸ₯‰ Third: URL Shortener by @dev_sarah


2-table schema
100K URLs, still instant
Built in one weekend
Clever use of PostgreSQL sequences for short codes

πŸ“Š Week 8 Achievements:
This Week's Focus: Production PostgreSQL

βœ… Monday: Deployment strategies demystified
βœ… Tuesday: Free monitoring stack
βœ… Wednesday: Admin Dashboard Kit launched!
βœ… Thursday: Scaling vs optimizing
βœ… Friday: YOUR amazing projects!

πŸ“ˆ Channel Growth Metrics:
Premium Content Success:


Admin Dashboard Kit (7 Stars): 8 purchases in 48 hours!


πŸš€ Week 9 Preview - Advanced PostgreSQL Patterns:
Monday: Multitenancy Patterns


Row-level security
Schema per tenant
Shared vs isolated

Tuesday: Time-Series in PostgreSQL


Partitioning strategies
Compression techniques
Real-time aggregation

Wednesday: [PREMIUM] API Rate Limiting System (6 Stars)


Complete rate limiting in PostgreSQL
No Redis needed
Per-user quotas
Sliding windows

Thursday: Event Sourcing


Audit everything
Time travel queries
GDPR compliance

Friday: Month recap & December planning

🎯 Weekend Challenge:
"Production Deployment"

Deploy something to production this weekend:


Use a $5-20 VPS
Set up backups
Add monitoring
Share your URL Monday!

Best deployment wins Week 9's premium content FREE!

πŸ’­ Your Feedback Needed:
What December content would help you most?
πŸ”΄ Building a complete SaaS series
🟑 PostgreSQL + AI/ML
🟒 Performance deep-dives
πŸ”΅ Migration guides (MySQL β†’ PostgreSQL)

πŸ™ Thank You!
Two weeks ago, we were at 89 members. Today: 200+!

Your engagement, questions, and projects make this community special.

Special shoutout to everyone who bought premium content - you're funding even better content ahead!

What did you ship this week?
Drop your wins below - no matter how small! πŸ‘‡

#PostgreSQL #Community #Showcase #WeekRecap #Shipping

@postgres
❀2
🏒 The 3 Ways to Build Multi-Tenant PostgreSQL (And Which One Won't Bankrupt You)

Building SaaS? You need to separate customer data. Here's the real story on multitenancy.

The Three Approaches:
Option 1: Shared Everything (Row-Level Security)

-- All customers in same tables
CREATE TABLE projects (
id UUID DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
name TEXT,
data JSONB
);

-- Enable RLS
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

-- Magic: Users only see their data
CREATE POLICY tenant_isolation ON projects
FOR ALL
USING (tenant_id = current_setting('app.current_tenant')::UUID);

-- In your app
SET LOCAL app.current_tenant = '123e4567-e89b-12d3-a456';
SELECT * FROM projects; -- Only tenant's projects!


Cost: $20/month for unlimited tenants
Good for: B2C, freemium, thousands of small customers

Option 2: Schema Per Tenant

-- Each customer gets their own schema
CREATE SCHEMA tenant_spotify;
CREATE SCHEMA tenant_netflix;

-- Same tables in each schema
CREATE TABLE tenant_spotify.projects (...);
CREATE TABLE tenant_netflix.projects (...);

-- In your app
SET search_path TO tenant_spotify;
SELECT * FROM projects; -- Isolated by schema


Cost: $20-100/month (depends on number of schemas)
Good for: B2B, 10-500 enterprise customers

Option 3: Database Per Tenant

-- Each customer gets own database
CREATE DATABASE customer_001;
CREATE DATABASE customer_002;

-- Complete isolation
-- Connect to specific database per request


Cost: $20/month per database (gets expensive FAST)
Good for: Enterprise, compliance requirements, <50 customers

🎯 The Solo Dev Reality Check:
You're not Salesforce. Start with Option 1 (RLS).

Real numbers from production:


5,000 tenants with RLS: Works perfectly
500 schemas: Starting to get painful
50+ databases: Nightmare maintenance

Complete RLS Implementation:
-- 1. Add tenant_id to EVERYTHING
ALTER TABLE users ADD COLUMN tenant_id UUID;
ALTER TABLE projects ADD COLUMN tenant_id UUID;
ALTER TABLE invoices ADD COLUMN tenant_id UUID;

-- 2. Create helper function
CREATE OR REPLACE FUNCTION set_current_tenant(p_tenant_id UUID)
RETURNS void AS $$
BEGIN
PERFORM set_config('app.current_tenant', p_tenant_id::TEXT, true);
END;
$$ LANGUAGE plpgsql;

-- 3. Row-level security policies
CREATE POLICY users_tenant_policy ON users
USING (tenant_id = current_setting('app.current_tenant')::UUID);

CREATE POLICY projects_tenant_policy ON projects
USING (tenant_id = current_setting('app.current_tenant')::UUID);

-- 4. Enforce for all queries
ALTER TABLE users FORCE ROW LEVEL SECURITY;
ALTER TABLE projects FORCE ROW LEVEL SECURITY;

-- 5. In your Node.js app
async function executeQuery(tenantId, query, params) {
await db.query('SELECT set_current_tenant($1)', [tenantId]);
return db.query(query, params);
}

// Now EVERY query is automatically filtered!
const projects = await executeQuery(
tenantId,
'SELECT * FROM projects' // No WHERE clause needed!
);


Performance Impact:
-- Without RLS: 0.8ms
SELECT * FROM projects WHERE tenant_id = '123';

-- With RLS: 0.9ms
SET LOCAL app.current_tenant = '123';
SELECT * FROM projects;

-- 12% overhead for bulletproof isolation? Worth it.


Migration Path as You Grow:
Stage 1 (0-100 customers): RLS
Stage 2 (100-1000): RLS with read replicas
Stage 3 (1000+): Consider schema separation for biggest customers
Stage 4 (Enterprise): Dedicated databases for whales

Common Pitfalls:
❌ Forgetting tenant_id on new tables
Solution: Create a base migration template

❌ Cross-tenant queries failing
Solution: Use SECURITY DEFINER functions for admin queries

❌ Performance degradation
Solution: Always index tenant_id FIRST in compound indexes

Your Multi-tenant Questions:
How many tenants are you planning for? What's your isolation requirement?

Tomorrow: Time-series data without TimescaleDB!

#PostgreSQL #Multitenancy #SaaS #RowLevelSecurity #Architecture

@postgres
1000 Subscribers Announcement Post

πŸŽ‰ 954 β†’ 1,000: Something BIG Is Coming...

We're 46 subscribers away from 1,000.

When we hit that number (probably this week), I'm doing something I've never done before.

Here's what I can tell you:

πŸ’Ž It involves Telegram's newest features
Not just content. Something more.

πŸ“š Everything changes for 48 hours
What was locked becomes unlocked. All of it.

🎲 Pure chance will favor some of you
No contests. No "best comment wins." Just fair, random luck.

⏰ It happens automatically at 1,000
The moment we cross that line, it begins.

Why 1,000 matters:

Two months ago: 89 solo devs learning PostgreSQL
Today: 954 developers saving $100K+/year collectively
At 1,000: We become one of the largest PostgreSQL communities on Telegram

This isn't just a number. It's validation that practical, no-BS PostgreSQL education works.

Your move:

If you know someone who:
- Pays too much for cloud databases
- Struggles with PostgreSQL optimization
- Wants to ship, not just learn

Send them here. Let's hit 1,000 together.

The faster we hit 1,000, the sooner [REDACTED] happens.

Trust me, you want to be here when it does. And you REALLY want to be online that weekend. πŸ“±

*Hint: Keep notifications on. Some things can't be claimed later.* πŸ’«

Let's make this week legendary.

#PostgreSQL #Community #Milestone #Soon

@postgres