Redis Object Caching for WordPress on a VPS: Install, Tune, and Measure the Speedup

Page caching hides the problem; object caching fixes it. On a 2 vCPU / 2 GB instance, moving WordPress persistent object cache from MySQL to Redis cut median database query time from 38 ms to 4 ms and reduced TTFB on uncached pages by roughly 45% in my tests. Here is the full setup, the tuning that matters, and how to prove the gain on your own box.

What object caching changes

Every get_option(), WP_Query and transient lookup normally round-trips to MySQL – and for a page-cache miss, that can be hundreds of queries. With a persistent object cache, the results live in RAM and survive between requests.

1 – Install Redis and the PHP client

sudo apt update
sudo apt -y install redis-server php-redis
sudo systemctl enable --now redis-server
redis-cli ping        # PONG
php -m | grep -i redis

Adjust the PHP version suffix if needed (php8.3-redis): the package name must match the FPM pool that serves the site.

2 – Tune redis.conf for a cache, not a database

sudo tee /etc/redis/redis.conf.d/vps-cache.conf <<'EOF'
unixsocket /run/redis/redis-server.sock
unixsocketperm 770
port 0
maxmemory 256mb
maxmemory-policy allkeys-lru
save ""
appendonly no
tcp-keepalive 60
EOF
sudo systemctl restart redis-server
redis-cli -s /run/redis/redis-server.sock ping
  • unix socket, port 0: removes TCP overhead and closes Redis to the network entirely.
  • allkeys-lru: evict the least recently used key under pressure – correct for cache, wrong for queues.
  • save “” + appendonly no: this data is reproducible; skip the disk writes.
  • maxmemory sizing: about 100 bytes of overhead per key plus value size. 256 MB holds roughly 1.2 million small WordPress keys.

3 – Wire it into WordPress

sudo -u www-data wp config set WP_REDIS_SCHEME unix --raw
sudo -u www-data wp config set WP_REDIS_PATH /run/redis/redis-server.sock
sudo -u www-data wp config set WP_REDIS_PREFIX "wpsite1:"
sudo -u www-data wp plugin install redis-cache --activate
sudo -u www-data wp redis enable
sudo -u www-data wp redis status

Give every site its own prefix – on a multi-site VPS a shared prefix silently mixes keys between installations. The plugin drops object-cache.php into wp-content/; keep it out of version control and note that it must be removed if the plugin is ever uninstalled, or WordPress will throw a fatal error.

4 – Verify the hit rate is real

redis-cli -s /run/redis/redis-server.sock info stats | \
  egrep 'keyspace_hits|keyspace_misses|evicted_keys'
redis-cli -s /run/redis/redis-server.sock info memory | egrep 'used_memory_human|maxmemory_human'
redis-cli -s /run/redis/redis-server.sock dbsize

A healthy object cache settles above 90% hits. Below 70% usually means a plugin is writing unique keys per request (often a poorly written slider or analytics plugin), and evicted_keys climbing fast means maxmemory is too small.

5 – Measure before and after

# warm the cache, then measure uncached pages
sudo -u www-data wp cache flush
ab -n 2000 -c 50 -H "Cookie: wordpress_logged_in_test=1" https://example.com/ \
   | egrep 'Requests per second|Time per request'
# query count from the app side
wp eval 'global $wpdb; echo $wpdb->num_queries;'
Metric (uncached page, 50 clients)MySQL cacheRedis object cache
Queries per request18241
Median DB time38 ms4 ms
TTFB (p50)610 ms335 ms
Requests/sec78142
MySQL CPU46%12%

Redis, Memcached, or APCu?

OptionScopeBest forCaveat
APCuSingle PHP processVery small single-worker sitesPer-process only, lost on every deploy
MemcachedNetwork, shared across poolsSimple key/value with a huge working setNo data structures, no persistence, no Lua
RedisUnix socket or networkObject cache plus sessions, queues, rate limitsRequires a correct maxmemory and eviction policy

For a single VPS, Redis over a unix socket wins on latency and lets the same instance serve sessions and background jobs. Memcached only earns its place when the working set is far larger than available RAM and cold restarts are acceptable.

Pitfalls that cost hours

  • Page cache plus object cache: keep the object cache for logged-in users and dynamic pages; do not disable your page cache thinking Redis replaces it.
  • Flushing on every publish: some plugins flush the whole namespace instead of invalidating keys, which turns Redis into extra latency.
  • Redis on the same saturated disk: with persistence disabled, Redis is RAM-only, but a memory-starved box will still swap. Keep 300-400 MB of headroom.
  • Monitoring: alert on used_memory above 80% of maxmemory and on evicted keys per minute.

Sizing the cache and reading the signals

redis-cli -s /run/redis/redis-server.sock info stats | egrep 'hits|misses|evicted'
redis-cli -s /run/redis/redis-server.sock --scan --pattern 'wpsite1:*' | wc -l
redis-cli -s /run/redis/redis-server.sock info memory | egrep 'used_memory_human|maxmemory_human'
  • Hit rate below 80%: a plugin is generating per-request keys or the TTL is too short. Inspect the largest key spaces with redis-cli --bigkeys.
  • evicted_keys climbing: raise maxmemory in 64 MB steps while memory headroom allows, then re-measure query counts.
  • Socket latency above 1 ms: look for a plugin issuing KEYS or FLUSHALL, and confirm persistence is still disabled for a pure cache.
  • Two sites on one instance: always a distinct prefix per installation, or one site flushes the other.

On a 1 GB instance, cap maxmemory near 128 MB and check available memory right after enabling the drop-in. On 2 GB and above, 256-384 MB is a reasonable ceiling that still leaves the database its own working set. Whatever number you pick, keep an alert on used_memory above 80% of the limit – eviction under a traffic spike is the failure mode that turns a speedup into a slowdown.

Objects cached in RAM are the cheapest performance win on any WordPress install – provided the instance has room for them. If your measurements show Redis being evicted during traffic peaks, the memory tier is the constraint; the VPS plans with full root access page lists RAM and vCPU options so you can size the cache before it starts thrashing.

Leave a Reply