PostgreSQL Performance Tuning: From Default to Optimized

PostgreSQL’s default configuration is designed to run on minimal hardware without crashing. It’s not designed to perform well. Out of the box, Postgres uses a fraction of available memory and I/O capacity. Proper tuning can improve performance by 10x or more. Memory Configuration shared_buffers The most important setting. This is Postgres’s own cache. 1 2 3 -- Default: 128MB (way too low) -- Recommended: 25% of system RAM shared_buffers = 4GB -- On a 16GB server Don’t set above 40% — the OS needs memory for its own file cache. ...

February 23, 2026 Â· 5 min Â· 1042 words Â· Rob Washington

Redis Patterns: Beyond Simple Caching

Redis gets introduced as a cache, but that undersells it. It’s an in-memory data structure server with atomic operations, pub/sub, streams, and more. These patterns show Redis’s real power. Basic Caching (The Familiar One) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import redis import json r = redis.Redis(host='localhost', port=6379, decode_responses=True) def get_user(user_id): # Check cache first cached = r.get(f"user:{user_id}") if cached: return json.loads(cached) # Miss: fetch from database user = db.query("SELECT * FROM users WHERE id = %s", user_id) # Cache with TTL r.setex(f"user:{user_id}", 3600, json.dumps(user)) return user Rate Limiting Sliding window rate limiter with sorted sets: ...

February 23, 2026 Â· 5 min Â· 1055 words Â· Rob Washington

JSON API Design: Conventions That Make Integration Easy

A well-designed API feels obvious. Endpoints are predictable, responses are consistent, errors are helpful. A poorly designed API creates confusion, support tickets, and workarounds. These conventions create APIs that are intuitive to integrate. Resource Naming Use nouns, not verbs. Plural for collections: ✅ G G P P D ❌ G P G P E E O U E E O E O G T T S T L B T S T S o T E a T T o T d d E : : / / / / / / / / / u u u u u g c u u s s s s s e r s s e e e e e t e e e r r r r r U a r r s s s s s s t / s / / / e e 1 / 1 1 1 r U 2 1 2 2 2 s s 3 2 3 3 3 e 3 r / d e l e t # # # # # e L G C U D i e r p e s t e d l t a a e u t t t u s e e e s e e r u u u r s s s s 1 e e e 2 r r r 3 1 1 2 2 3 3 Nested resources for relationships: ...

February 23, 2026 Â· 13 min Â· 2611 words Â· Rob Washington

Technical Writing for Developers: Documentation That Gets Read

You can write brilliant code that nobody can use because the documentation is impenetrable. Or you can write average code with excellent docs that becomes everyone’s go-to solution. Documentation is a multiplier. These principles help you write docs that work. Know Your Audience Different readers need different docs: Tutorials (learning-oriented): “Follow these steps to build X” For newcomers Hand-holding is okay Complete, working examples Progressive complexity How-to Guides (task-oriented): “How to do X” ...

February 23, 2026 Â· 13 min Â· 2701 words Â· Rob Washington

Nginx Configuration Patterns: From Basic Proxy to Production-Ready

Nginx is everywhere: reverse proxy, load balancer, static file server, SSL terminator. Its configuration syntax is powerful but has gotchas that catch everyone at least once. These patterns cover common use cases done right. Basic Reverse Proxy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 upstream backend { server 127.0.0.1:3000; } server { listen 80; server_name example.com; location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } Always set those headers — your backend needs to know the real client IP and protocol. ...

February 23, 2026 Â· 5 min Â· 1043 words Â· Rob Washington

Secrets Management Patterns for Modern Infrastructure

Every infrastructure team eventually faces the same uncomfortable question: where do the secrets go? API keys, database passwords, TLS certificates, OAuth tokens — they all need to live somewhere. The wrong answer (“in the repo, it’s fine, it’s private”) creates technical debt that compounds silently until someone accidentally pushes to public or an ex-employee still has access to production credentials. The Anti-Patterns First Environment variables everywhere. Yes, the twelve-factor app says config comes from the environment. But “the environment” doesn’t mean a .env file committed to git. Environment variables are runtime config, not secret storage. ...

February 23, 2026 Â· 4 min Â· 709 words Â· Rob Washington

Systemd Service Management: Running Applications Reliably

Systemd is how modern Linux manages services. It starts your applications at boot, restarts them when they crash, and handles dependencies between services. Understanding systemd transforms “it works when I run it manually” into “it runs reliably in production.” Basic Service Unit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # /etc/systemd/system/myapp.service [Unit] Description=My Application After=network.target [Service] Type=simple User=appuser WorkingDirectory=/opt/myapp ExecStart=/opt/myapp/bin/server Restart=always RestartSec=5 [Install] WantedBy=multi-user.target 1 2 3 4 # Enable and start sudo systemctl daemon-reload sudo systemctl enable myapp sudo systemctl start myapp Service Types simple (default): Process started by ExecStart is the main process. ...

February 23, 2026 Â· 5 min Â· 915 words Â· Rob Washington

Backup Strategies: Because Hope Is Not a Disaster Recovery Plan

Everyone has backups. Few have tested restores. The backup that fails during a crisis is worse than no backup — it gave you false confidence. Backup strategy isn’t about the backup. It’s about the restore. The 3-2-1 Rule A minimum viable backup strategy: 3 copies of your data 2 different storage media/types 1 copy offsite P C C C r o o o i p p p m y y y a r 1 2 3 y : : : : P L R O r o e f o c m f d a o s u l t i c e t t s e i n r o a e a n p p r s l c d h i h a o c i t t a v a e b ( ( a s d ( s a i d e m f i e f f e f d r e a e r t n e a t n c t e r n e p t g r e i o r o v ) n i ) d e r ) What to Back Up Always: ...

February 23, 2026 Â· 6 min Â· 1110 words Â· Rob Washington

SSH Security Hardening: Protecting Your Most Critical Access Point

SSH is the front door to your infrastructure. Every server, every cloud instance, every piece of automation relies on it. A compromised SSH setup means game over. These hardening steps dramatically reduce your attack surface. Disable Password Authentication Passwords can be brute-forced. Keys can’t (practically): 1 2 3 4 # /etc/ssh/sshd_config PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no 1 2 # Restart SSH (keep your current session open!) sudo systemctl restart sshd Before doing this, ensure your key is in ~/.ssh/authorized_keys. ...

February 23, 2026 Â· 5 min Â· 904 words Â· Rob Washington

Linux Performance Debugging: Finding What's Slow and Why

Something’s slow. Users are complaining. Your monitoring shows high latency but not why. You SSH into the server and need to figure out what’s wrong — fast. This is a systematic approach to Linux performance debugging. Start with the Big Picture Before diving deep, get an overview: 1 2 3 4 5 6 # System load and uptime uptime # 17:00:00 up 45 days, load average: 8.52, 4.23, 2.15 # Load average: 1/5/15 minute averages # Compare to CPU count: load 8 on 4 CPUs = overloaded 1 2 # Quick health check top -bn1 | head -20 CPU: Who’s Using It? 1 2 3 4 5 6 # Real-time CPU usage top # Sort by CPU: press 'P' # Sort by memory: press 'M' # Show threads: press 'H' 1 2 3 4 5 # CPU usage by process (snapshot) ps aux --sort=-%cpu | head -10 # CPU time accumulated ps aux --sort=-time | head -10 1 2 3 4 # Per-CPU breakdown mpstat -P ALL 1 5 # Shows each CPU core's utilization # Look for: one core at 100% (single-threaded bottleneck) 1 2 3 4 5 6 7 8 9 10 11 # What's the CPU actually doing? vmstat 1 5 # r b swpd free buff cache si so bi bo in cs us sy id wa st # 3 0 0 245612 128940 2985432 0 0 8 24 312 892 45 12 40 3 0 # r: processes waiting for CPU (high = CPU-bound) # b: processes blocked on I/O (high = I/O-bound) # us: user CPU time # sy: system CPU time # wa: waiting for I/O # id: idle Memory: Running Out? 1 2 3 4 5 6 7 # Memory overview free -h # total used free shared buff/cache available # Mem: 31Gi 12Gi 2.1Gi 1.2Gi 17Gi 17Gi # "available" is what matters, not "free" # Linux uses free memory for cache — that's good 1 2 3 4 5 6 7 8 # Top memory consumers ps aux --sort=-%mem | head -10 # Memory details per process pmap -x <pid> # Or from /proc cat /proc/<pid>/status | grep -i mem 1 2 3 # Check for OOM killer activity dmesg | grep -i "out of memory" journalctl -k | grep -i oom 1 2 3 # Swap usage (if swapping, you're in trouble) swapon -s vmstat 1 5 # si/so columns show swap in/out Disk I/O: The Hidden Bottleneck 1 2 3 4 5 6 7 8 9 10 11 # I/O wait in top top # Look at %wa in the CPU line # Detailed I/O stats iostat -xz 1 5 # Device r/s w/s rkB/s wkB/s await %util # sda 150.00 200.00 4800 8000 12.5 85.0 # await: average I/O wait time (ms) - high = slow disk # %util: disk utilization - 100% = saturated 1 2 3 4 5 6 # Which processes are doing I/O? iotop -o # Shows only processes actively doing I/O # Or without iotop pidstat -d 1 5 1 2 3 # Check for disk errors dmesg | grep -i error smartctl -a /dev/sda # SMART data for disk health Network: Connections and Throughput 1 2 3 4 5 6 7 8 # Network interface stats ip -s link show # Watch for errors, drops, overruns # Real-time bandwidth iftop # Or nload 1 2 3 4 5 6 7 # Connection states ss -s # Total: 1523 (kernel 1847) # TCP: 892 (estab 654, closed 89, orphaned 12, timewait 127) # Many TIME_WAIT: connection churn # Many ESTABLISHED: legitimate load or connection leak 1 2 3 4 5 # Connections per state ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn # Connections by remote IP ss -tn | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head 1 2 3 4 5 6 # Check for packet loss ping -c 100 <gateway> # Any loss = network problem # TCP retransmits netstat -s | grep -i retrans Process-Level Debugging 1 2 3 4 5 6 # What's a specific process doing? strace -p <pid> -c # Summary of system calls strace -p <pid> -f -e trace=network # Network-related calls only 1 2 3 4 5 6 7 8 # Open files and connections lsof -p <pid> # Just network connections lsof -i -p <pid> # Files in a directory lsof +D /var/log 1 2 3 4 5 # Process threads ps -T -p <pid> # Thread CPU usage top -H -p <pid> System-Wide Tracing 1 2 3 4 5 6 7 # What's happening system-wide? perf top # Real-time view of where CPU cycles go # Record and analyze perf record -g -a sleep 10 perf report 1 2 3 4 5 6 7 8 9 # BPF-based tools (if available) # CPU usage by function profile-bpfcc 10 # Disk latency histogram biolatency-bpfcc # TCP connection latency tcpconnlat-bpfcc The Checklist Approach When debugging, go through systematically: ...

February 23, 2026 Â· 6 min Â· 1095 words Â· Rob Washington