SQLite configuration
Laju Go uses SQLite with optimizations that can be customized based on:
- Available RAM — determines cache size and connection pool
- CPU cores — determines concurrent connections
- Storage type — NVMe vs SSD vs HDD (mmap optimization)
- Traffic pattern — read-heavy vs write-heavy workloads
Current configuration (default)
Section titled “Current configuration (default)”The default configuration in cmd/laju-go/main.go is optimized for a 1–2 GB RAM VPS with NVMe:
// cmd/laju-go/main.go — initDatabase()db.SetMaxOpenConns(15) // Connection pool sizedb.SetMaxIdleConns(10) // Keep idle connections readydb.SetConnMaxLifetime(5 * time.Minute) // Connection lifetime
db.Exec("PRAGMA journal_mode = WAL") // Write-Ahead Loggingdb.Exec("PRAGMA synchronous = NORMAL") // Balance speed/durabilitydb.Exec("PRAGMA cache_size = -16000") // 16 MB cachedb.Exec("PRAGMA mmap_size = 268435456") // 256 MB mmapdb.Exec("PRAGMA temp_store = MEMORY") // Memory temp tablesdb.Exec("PRAGMA busy_timeout = 5000") // 5 second lock waitdb.Exec("PRAGMA wal_autocheckpoint = 1000") // Checkpoint frequencyConfiguration by RAM size
Section titled “Configuration by RAM size”1. Small server (512 MB – 1 GB RAM)
Section titled “1. Small server (512 MB – 1 GB RAM)”Use case: development, low-traffic staging, MVP
// Connection poolingdb.SetMaxOpenConns(10) // Conservative pooldb.SetMaxIdleConns(3) // Minimal idledb.SetConnMaxLifetime(5 * time.Minute)
// SQLite PRAGMAsdb.Exec("PRAGMA cache_size = -8000") // 8 MB cache (~1.5% of 512 MB)db.Exec("PRAGMA mmap_size = 134217728") // 128 MB mmap (virtual)db.Exec("PRAGMA busy_timeout = 5000") // 5 second timeoutdb.Exec("PRAGMA wal_autocheckpoint = 1000") // Default checkpointdb.Exec("PRAGMA synchronous = NORMAL") // Safe for WALdb.Exec("PRAGMA journal_mode = WAL") // WAL modedb.Exec("PRAGMA temp_store = MEMORY") // Memory temp tablesMemory breakdown:
App (Go/Fiber) : ~200-300 MBSQLite cache : 8 MBSQLite mmap (virt) : 128 MB (not physical RAM)OS overhead : ~150 MBBuffer/Headroom : ~50-100 MB─────────────────────────────────Total : ~400-500 MB of 512 MB (80-95% usage)2. Medium server (2 GB – 4 GB RAM) — recommended
Section titled “2. Medium server (2 GB – 4 GB RAM) — recommended”Use case: production apps, medium traffic (10k–50k users/day)
// Connection poolingdb.SetMaxOpenConns(25) // Moderate pooldb.SetMaxIdleConns(10) // Keep connections warmdb.SetConnMaxLifetime(5 * time.Minute)
// SQLite PRAGMAsdb.Exec("PRAGMA cache_size = -32000") // 32 MB cache (~1% of 4 GB)db.Exec("PRAGMA mmap_size = 536870912") // 512 MB mmapdb.Exec("PRAGMA busy_timeout = 7500") // 7.5 second timeoutdb.Exec("PRAGMA wal_autocheckpoint = 2000") // Less frequent checkpointdb.Exec("PRAGMA synchronous = NORMAL") // Safe for WALdb.Exec("PRAGMA journal_mode = WAL") // WAL modedb.Exec("PRAGMA temp_store = MEMORY") // Memory temp tablesMemory breakdown:
App (Go/Fiber) : ~300-400 MBSQLite cache : 32 MBSQLite mmap (virt) : 512 MB (virtual)OS overhead : ~200 MBBuffer/Headroom : ~1 GB+─────────────────────────────────Total : ~600-700 MB of 2-4 GB (20-35% usage)Expected performance:
- Max RPS: ~15,000–25,000
- Concurrent users: 500–1,000
- Cache hit ratio: ~85–90%
- P99 latency: ~30–50 ms
3. Large server (8 GB – 16 GB RAM)
Section titled “3. Large server (8 GB – 16 GB RAM)”Use case: high-traffic production (50k–200k users/day)
// Connection poolingdb.SetMaxOpenConns(50) // Large pooldb.SetMaxIdleConns(15) // More idle connectionsdb.SetConnMaxLifetime(10 * time.Minute) // Longer lifetime
// SQLite PRAGMAsdb.Exec("PRAGMA cache_size = -500000") // 500 MB cache (~3% of 16 GB)db.Exec("PRAGMA mmap_size = 1073741824") // 1 GB mmapdb.Exec("PRAGMA busy_timeout = 10000") // 10 second timeoutdb.Exec("PRAGMA wal_autocheckpoint = 3000") // Less frequent checkpointdb.Exec("PRAGMA synchronous = NORMAL") // Safe for WALdb.Exec("PRAGMA journal_mode = WAL") // WAL modedb.Exec("PRAGMA temp_store = MEMORY") // Memory temp tablesMemory breakdown:
App (Go/Fiber) : ~400-600 MBSQLite cache : 500 MBSQLite mmap (virt) : 1 GB (virtual)OS overhead : ~300 MBBuffer/Headroom : ~6-10 GB─────────────────────────────────Total : ~1.5-2 GB of 8-16 GB (10-25% usage)Expected performance:
- Max RPS: ~40,000–60,000
- Concurrent users: 2,000–3,000
- Cache hit ratio: ~92–95%
- P99 latency: ~15–25 ms
4. Enterprise server (32 GB – 64 GB RAM)
Section titled “4. Enterprise server (32 GB – 64 GB RAM)”Use case: enterprise production (200k–1M+ users/day)
// Connection poolingdb.SetMaxOpenConns(100) // Maximum pool for 16+ vCPUdb.SetMaxIdleConns(25) // Many idle connectionsdb.SetConnMaxLifetime(10 * time.Minute) // Long lifetime
// SQLite PRAGMAsdb.Exec("PRAGMA cache_size = -4000000") // 4 GB cache (~7% of 58 GB)db.Exec("PRAGMA mmap_size = 2147483648") // 2 GB mmap (NVMe optimization)db.Exec("PRAGMA busy_timeout = 10000") // 10 second timeoutdb.Exec("PRAGMA wal_autocheckpoint = 5000") // Minimal checkpoint overheaddb.Exec("PRAGMA synchronous = NORMAL") // Safe for WALdb.Exec("PRAGMA journal_mode = WAL") // WAL modedb.Exec("PRAGMA temp_store = MEMORY") // Memory temp tablesdb.Exec("PRAGMA page_size = 4096") // Explicit page sizeMemory breakdown (example: 58 GB RAM):
App (Go/Fiber) : ~500-800 MBSQLite cache : 4 GBSQLite mmap (virt) : 2 GB (virtual)OS overhead : ~500 MBBuffer/Headroom : ~50 GB+─────────────────────────────────Total : ~5-6 GB of 58 GB (10% usage)Expected performance:
- Max RPS: ~80,000–120,000
- Concurrent users: 5,000–10,000
- Cache hit ratio: ~96–99%
- P99 latency: ~8–15 ms
Configuration by workload
Section titled “Configuration by workload”Read-heavy workload (>90% reads)
Section titled “Read-heavy workload (>90% reads)”Optimization for maximum caching:
// Increase cache sizedb.Exec("PRAGMA cache_size = -1000000") // 1 GB cachedb.Exec("PRAGMA mmap_size = 2147483648") // 2 GB mmap
// Aggressive connection poolingdb.SetMaxOpenConns(50)db.SetMaxIdleConns(25)Reason: more data cached = less disk I/O.
Write-heavy workload (>30% writes)
Section titled “Write-heavy workload (>30% writes)”Optimization for write throughput:
// Reduce checkpoint frequencydb.Exec("PRAGMA wal_autocheckpoint = 10000") // Less frequent checkpoints
// Keep cache moderate (WAL needs memory too)db.Exec("PRAGMA cache_size = -32000") // 32 MB cache
// Increase busy timeout for write contentiondb.Exec("PRAGMA busy_timeout = 15000") // 15 second timeoutReason: less frequent checkpoints = better write performance.
Mixed workload (70% reads, 30% writes)
Section titled “Mixed workload (70% reads, 30% writes)”Balanced configuration (default is sufficient):
db.SetMaxOpenConns(25)db.SetMaxIdleConns(10)db.Exec("PRAGMA cache_size = -32000") // 32 MBdb.Exec("PRAGMA wal_autocheckpoint = 2000") // BalancedPRAGMA reference
Section titled “PRAGMA reference”Connection pooling
Section titled “Connection pooling”| Setting | Recommended range | Description |
|---|---|---|
MaxOpenConns |
10–100 | Max concurrent DB connections |
MaxIdleConns |
3–25 | Idle connections to keep |
ConnMaxLifetime |
5–10 min | Connection reuse duration |
Rule of thumb: MaxOpenConns = 6–8 × CPU cores
Cache size
Section titled “Cache size”PRAGMA cache_size = -<KB> -- Negative value = KB| RAM | Cache size | Value |
|---|---|---|
| 512 MB | 8 MB | -8000 |
| 1 GB | 16 MB | -16000 |
| 2 GB | 32 MB | -32000 |
| 4 GB | 64 MB | -64000 |
| 8 GB | 256 MB | -256000 |
| 16 GB | 500 MB | -500000 |
| 32 GB | 2 GB | -2000000 |
| 58 GB | 4 GB | -4000000 |
Rule of thumb: 1–7% of total RAM.
Mmap size
Section titled “Mmap size”PRAGMA mmap_size = <bytes>| Storage type | Mmap size | Value |
|---|---|---|
| HDD | 64 MB | 67108864 |
| SSD | 256 MB | 268435456 |
| NVMe | 1–2 GB | 1073741824 – 2147483648 |
Reason: NVMe benefits greatly from memory-mapped I/O.
Busy timeout
Section titled “Busy timeout”PRAGMA busy_timeout = <milliseconds>| Concurrency | Timeout | Value |
|---|---|---|
| Low (<100 RPS) | 3 seconds | 3000 |
| Medium (100–1000 RPS) | 5–7 seconds | 5000–7500 |
| High (>1000 RPS) | 10 seconds | 10000 |
| Very high (write contention) | 15 seconds | 15000 |
WAL autocheckpoint
Section titled “WAL autocheckpoint”PRAGMA wal_autocheckpoint = <pages>| Workload | Checkpoint | Value |
|---|---|---|
| Read-heavy | 1000 pages | 1000 (default) |
| Mixed | 2000–3000 pages | 2000–3000 |
| Write-heavy | 5000–10000 pages | 5000–10000 |
Trade-off: less frequent = better write perf, more disk usage.
Synchronous
Section titled “Synchronous”PRAGMA synchronous = FULL | NORMAL | OFF| Mode | Safety | Performance | Use case |
|---|---|---|---|
FULL |
Maximum | Slowest | Financial/critical data |
NORMAL |
Very high | Fast | Production web apps (recommended) |
OFF |
Low | Fastest | Development only (risky!) |
Verification
Section titled “Verification”Check current settings
Section titled “Check current settings”# Connect to databasesqlite3 data/app.db
# Check all settingsPRAGMA journal_mode; -- Should be: walPRAGMA synchronous; -- Should be: 1 (NORMAL)PRAGMA cache_size; -- Should be: -<value>PRAGMA mmap_size; -- Should be: <value>PRAGMA busy_timeout; -- Should be: <value>PRAGMA wal_autocheckpoint; -- Should be: <value>PRAGMA temp_store; -- Should be: 2 (MEMORY)
# Exit.exitVerify from application logs
Section titled “Verify from application logs”When the server starts, you’ll see:
SQLite optimizations: journal_mode=WAL, synchronous=NORMAL, cache_size=32000KB, mmap_size=536870912KB, wal_autocheckpoint=2000, busy_timeout=7500msTroubleshooting
Section titled “Troubleshooting”Database locked errors
Section titled “Database locked errors”Symptoms: database is locked, PRAGMA busy_timeout doesn’t help
Solutions:
- Increase busy_timeout:
PRAGMA busy_timeout = 15000 - Reduce MaxOpenConns:
db.SetMaxOpenConns(10) - Check for long-running transactions
- Enable WAL mode:
PRAGMA journal_mode = WAL
High memory usage
Section titled “High memory usage”Symptoms: OOM killer, server swap thrashing
Solutions:
- Reduce cache_size:
PRAGMA cache_size = -8000(8 MB) - Reduce MaxOpenConns:
db.SetMaxOpenConns(10) - Reduce mmap_size:
PRAGMA mmap_size = 134217728(128 MB)
Slow queries
Section titled “Slow queries”Symptoms: P99 latency > 100 ms
Solutions:
- Increase cache_size:
PRAGMA cache_size = -64000(64 MB) - Add indexes for frequent queries
- Run ANALYZE:
sqlite3 data/app.db "ANALYZE;" - Check query plans:
EXPLAIN QUERY PLAN <your-query>
Write contention
Section titled “Write contention”Symptoms: timeouts on write operations, WAL file grows large
Solutions:
- Increase wal_autocheckpoint:
PRAGMA wal_autocheckpoint = 5000 - Run manual checkpoint:
PRAGMA wal_checkpoint(PASSIVE) - Reduce concurrent writes (queue/batch writes)
- Consider PostgreSQL if write-heavy
Migration guide
Section titled “Migration guide”From development to production
Section titled “From development to production”Step 1: Update configuration in cmd/laju-go/main.go:
// Change from development (512MB-1GB) to production (2-4GB)db.SetMaxOpenConns(10) // → 25db.SetMaxIdleConns(3) // → 10
db.Exec("PRAGMA cache_size = -8000") // → -32000 (32MB)db.Exec("PRAGMA mmap_size = 134217728") // → 536870912 (512MB)db.Exec("PRAGMA busy_timeout = 5000") // → 7500 (7.5s)db.Exec("PRAGMA wal_autocheckpoint = 1000") // → 2000Step 2: Deploy and monitor:
# Deploygit push && ssh user@server "cd laju-go && git pull && go build && sudo systemctl restart laju-go"
# Monitor logsjournalctl -u laju-go -f
# Check SQLite settingssqlite3 data/app.db "PRAGMA cache_size;"From SQLite to PostgreSQL (future scaling)
Section titled “From SQLite to PostgreSQL (future scaling)”When to migrate:
- Write-heavy workload (>30% writes)
-
10,000 concurrent users
- Need horizontal scaling
- Need high-availability (multi-region)
Migration steps:
- Install PostgreSQL
- Update connection string in
.env - Replace SQLite-specific PRAGMAs with PostgreSQL equivalents
- Run migrations
- Test thoroughly
Performance benchmarks
Section titled “Performance benchmarks”Vultr HF 1 GB (1 vCPU, 1 GB RAM, NVMe)
Section titled “Vultr HF 1 GB (1 vCPU, 1 GB RAM, NVMe)”Configuration:- MaxOpenConns: 10- cache_size: 8 MB- mmap_size: 128 MB
Benchmark (hey -n 1000 -c 10):- Requests/sec: ~3,500- P50 latency: 15 ms- P95 latency: 45 ms- P99 latency: 80 msVultr HF 2 GB (2 vCPU, 2 GB RAM, NVMe)
Section titled “Vultr HF 2 GB (2 vCPU, 2 GB RAM, NVMe)”Configuration:- MaxOpenConns: 25- cache_size: 32 MB- mmap_size: 512 MB
Benchmark (hey -n 10000 -c 50):- Requests/sec: ~15,000- P50 latency: 8 ms- P95 latency: 25 ms- P99 latency: 45 msVultr HF 4 GB (4 vCPU, 4 GB RAM, NVMe)
Section titled “Vultr HF 4 GB (4 vCPU, 4 GB RAM, NVMe)”Configuration:- MaxOpenConns: 50- cache_size: 64 MB- mmap_size: 1 GB
Benchmark (hey -n 50000 -c 100):- Requests/sec: ~35,000- P50 latency: 5 ms- P95 latency: 15 ms- P99 latency: 25 msVultr HF 8 GB (6 vCPU, 8 GB RAM, NVMe)
Section titled “Vultr HF 8 GB (6 vCPU, 8 GB RAM, NVMe)”Configuration:- MaxOpenConns: 75- cache_size: 256 MB- mmap_size: 1 GB
Benchmark (hey -n 100000 -c 200):- Requests/sec: ~60,000- P50 latency: 3 ms- P95 latency: 10 ms- P99 latency: 18 msVultr HF 16 GB / 58 GB (16 vCPU, 16–58 GB RAM, NVMe)
Section titled “Vultr HF 16 GB / 58 GB (16 vCPU, 16–58 GB RAM, NVMe)”Configuration:- MaxOpenConns: 100- cache_size: 4 GB- mmap_size: 2 GB
Expected (hey -n 200000 -c 500):- Requests/sec: ~100,000-120,000- P50 latency: 2 ms- P95 latency: 8 ms- P99 latency: 12 msBest practices
Section titled “Best practices”1. Start conservative, scale up
Section titled “1. Start conservative, scale up”// Start with minimal configurationdb.SetMaxOpenConns(10)db.Exec("PRAGMA cache_size = -8000")
// Monitor usage for 1-2 weeks// Increase if needed2. Monitor memory usage
Section titled “2. Monitor memory usage”# Check memory usageps aux | grep laju-go
# Check SQLite memorysqlite3 data/app.db "PRAGMA cache_size;"3. Test before production
Section titled “3. Test before production”# Load test with new configurationhey -n 10000 -c 50 http://localhost:8080/
# Monitor response times# Check for errors4. Document changes
Section titled “4. Document changes”Always document configuration changes:
- Date of change
- Reason for change
- Before/after values
- Performance impact
5. Keep WAL files managed
Section titled “5. Keep WAL files managed”# Check WAL file sizels -lh data/app.db*
# Manual checkpoint if WAL is too largesqlite3 data/app.db "PRAGMA wal_checkpoint(PASSIVE);"Quick reference table
Section titled “Quick reference table”| Server RAM | MaxOpenConns | cache_size | mmap_size | busy_timeout | wal_autocheckpoint |
|---|---|---|---|---|---|
| 512 MB | 10 | 8 MB | 128 MB | 5000 | 1000 |
| 1 GB | 15 | 16 MB | 256 MB | 5000 | 1000 |
| 2 GB | 25 | 32 MB | 512 MB | 7500 | 2000 |
| 4 GB | 50 | 64 MB | 1 GB | 10000 | 3000 |
| 8 GB | 75 | 256 MB | 1 GB | 10000 | 3000 |
| 16 GB | 100 | 500 MB | 2 GB | 10000 | 5000 |
| 32 GB | 100 | 2 GB | 2 GB | 10000 | 5000 |
| 58 GB | 100 | 4 GB | 2 GB | 10000 | 5000 |