srmdn.

Back

Before You Add RedisBlur image

Your app feels slow. The pages take a second too long, and someone mentioned Redis. Before you wire up a new service, check two things: whether nginx is compressing your responses, and whether your SQLite WAL is growing unchecked. Either one will make an app feel slow, and both take minutes to fix.

This article is written for apps running SQLite and nginx: a common setup for small to mid-size web apps deployed on a single server. The gzip and Redis sections apply regardless of your database. The WAL section is SQLite-specific; if you’re on Postgres or MySQL, skip it.

Redis is a real solution. But it solves a specific problem: repeated expensive computation. If that’s not your problem, adding Redis adds complexity without fixing anything.

Where the Slowness Lives#

Performance problems in a web app come from three distinct places:

Browser <—— network ——> nginx <—— proxy ——> app <—— query ——> database
               ↑                                       ↑
             gzip                                     WAL

                                      Redis
plaintext

Gzip lives between nginx and the browser. WAL lives inside SQLite. Redis lives between your app and the database. They don’t overlap.

A 30KB HTML page sent without compression is a network problem. Adding Redis doesn’t help it. A SQLite database with an unchecked WAL file is a read latency problem. Gzip doesn’t help it. A query that takes 200ms and runs 50 times per second is a computation problem. That’s where Redis fits.

Gzip: The Free Win#

Nginx can compress responses before sending them. Enable it in your server block and a 30KB HTML page becomes 7KB. Over a slow mobile connection, that’s the difference between a page that loads and one that spins.

The config:

gzip on;
gzip_proxied any;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
nginx

gzip_proxied any is the directive most people miss. Without it, nginx only compresses files it serves directly from disk. If your app runs behind a reverse proxy (it probably does), nginx won’t compress its responses unless you set this.

Two things gzip won’t help: responses smaller than 1KB (the compression overhead isn’t worth it) and binary formats like images and video (they’re already compressed; gzip will make them larger).

WAL: The One-Line SQLite Fix#

This section applies to SQLite only. Postgres and MySQL handle concurrency differently and don’t have a WAL checkpoint problem in the same sense.

SQLite has two main journaling modes. The default (DELETE) locks the entire database file on every write. Any reader waiting for that lock blocks until the write finishes. For a web app handling concurrent requests, this creates visible latency under load.

WAL mode (Write-Ahead Log) separates reads from writes. Instead of modifying the database file directly, SQLite appends changes to a .wal file. Reads see a consistent snapshot of the main database without waiting for writes to finish.

Enable it once at connection setup:

PRAGMA journal_mode = WAL;
sql

The catch: SQLite flushes WAL changes back to the main database (a “checkpoint”) only when the WAL reaches 1000 pages by default. Until that threshold is hit, every read has to scan both the main database and the entire WAL to reconstruct the latest state. A WAL that grows for days before checkpointing can reach several megabytes, and reads slow down proportionally.

Lowering the threshold keeps the WAL small:

PRAGMA wal_autocheckpoint = 100;
sql

Set this at connection init, alongside journal_mode. The WAL checkpoints every 100 pages and stays under 400KB. There’s no meaningful downside unless you’re on a write-heavy workload where frequent checkpoints create contention, which is uncommon for typical web apps.

Redis: When You Actually Need It#

Redis is an in-memory key-value store. You run a database query once, store the result in Redis with an expiry, and serve subsequent requests from memory instead of running the query again.

That’s the core use case. If you’re not running the same expensive query repeatedly, Redis adds a service to deploy, monitor, and keep synchronized with your database without improving response times.

The cases where it genuinely helps: aggregation or ranking queries that run on every page load, session data that needs to be shared across multiple app instances, or any computation that takes hundreds of milliseconds and produces a result that stays valid for minutes.

The cases where it doesn’t: simple lookups by primary key (already fast), apps where queries run a few dozen times per minute, or datasets small enough to fit in application memory.

Cache invalidation is what nobody mentions in the tutorials. Cached data goes stale. You need to decide when to expire it, whether to delete it on writes or wait for the TTL, and what happens when a cache miss occurs under load. None of this is complicated, but it’s all code you write and bugs you debug. The cost is real.

Common Gotchas#

LayerMistakeEffect
GzipMissing gzip_proxied anyProxied app responses aren’t compressed
GzipCompressing images and videoResponse gets larger, not smaller
WALDefault checkpoint threshold of 1000WAL grows for days; reads slow down
WALMultiple concurrent writersWAL allows one writer at a time; extra writers queue up
RedisCaching without an expiryStale data served indefinitely
RedisNot invalidating on writesCache and database diverge silently

Is This Right for You?#

Add gzip if you’re serving HTML, CSS, or JSON over nginx and haven’t confirmed Content-Encoding: gzip in your response headers. It’s a config change and a reload.

Set wal_autocheckpoint = 100 if you’re using SQLite in WAL mode on a web app with concurrent requests. One pragma at connection init, no schema changes, no migration.

Add Redis if a specific query is measurably slow, runs on every request, and the result stays valid long enough to be worth caching. Profile the query first. If it completes in under 20ms and runs a few hundred times per day, you don’t have a database problem.

Most apps I’ve seen that feel slow are sending full HTML payloads uncompressed over the wire. The database is fine. Fix the network layer before adding infrastructure.

Enjoyed this post?

Get Linux tips, sysadmin war stories, and new posts delivered to your inbox.

No spam. Unsubscribe anytime.

Before You Add Redis
https://srmdn.com/blog/before-you-add-redis
Author srmdn
Published at March 31, 2026