A page cache (LSCache, Varnish, NGINX FastCGI) skips PHP entirely on cache hits. An
object cache does the opposite — it speeds up uncacheable requests by holding the
results of every wp_options lookup, every get_post_meta call, and every transient in
RAM instead of MySQL. On a WooCommerce checkout, a logged-in WordPress admin, or a
plugin-heavy membership site, swapping the default in-memory PHP cache for Redis cuts
admin-area TTFB by 40-70% and drops database CPU on the box by 20-40%.
This guide installs Redis on a cPanel server, enables the phpredis extension across
every selectable PHP version, and wires WordPress to a single Redis instance with
per-account key prefixes so accounts can't read each other's cache. It assumes
AlmaLinux 8/9 or CloudLinux 8/9 with an active cPanel licence.
When Redis is worth the effort
Object cache pays off on three workloads:
- WooCommerce or any ecommerce on WordPress. Cart pages, checkout, account dashboards, and admin order screens never enter the page cache. Redis is the only layer that helps them.
- Membership, LMS, and LearnDash sites. Logged-in traffic is the whole product — page caching is irrelevant, object caching is everything.
- WordPress multisite networks. Network-wide options live in
wp_sitemetaand get hit on every request. Redis cuts that to one network round-trip per worker boot.
If a site is 95% anonymous brochure traffic and already running LSCache, Redis adds maybe 5-10 ms savings on the edge cases. Don't bother — the operational overhead isn't worth it.
Prerequisites
- WHM root access
- cPanel running on AlmaLinux 8/9 or CloudLinux 8/9
- EasyApache 4 with at least one PHP version 8.1 or newer
- Free RAM equal to roughly 256-512 MB per 100 active WordPress sites (Redis will be configured to cap itself, but the kernel needs the headroom)
Step 1 — Install Redis from EPEL
The base AlmaLinux repos ship a usable Redis, but EPEL tracks newer point releases.
dnf install -y epel-release
dnf install -y redis
systemctl enable --now redis
Confirm it bound to the loopback only:
ss -tlnp | grep 6379
You should see 127.0.0.1:6379, never 0.0.0.0:6379. If Redis is listening on a
public interface on a shared server, every cPanel user can redis-cli into it from
their account and read every other site's cache. Fix it now.
Step 2 — Configure Redis for shared hosting
Edit /etc/redis/redis.conf (path may be /etc/redis.conf on older builds):
bind 127.0.0.1
port 6379
unixsocket /var/run/redis/redis.sock
unixsocketperm 770
maxmemory 1gb
maxmemory-policy allkeys-lru
save ""
appendonly no
requirepass <generate-a-32-char-password>
The values that matter:
maxmemory 1gb— hard cap. Redis will evict, not OOM-kill the box. Size it at roughly 5-10 MB per active WordPress site, then round up.allkeys-lru— when full, evict the least-recently-used key regardless of TTL. This is the only correct policy for an object cache; the defaultnoevictionwill silently start failing writes once the cap is hit.save ""andappendonly no— disable disk persistence. Object cache is ephemeral by design; flushing it on Redis restart is the correct behaviour and saves significant disk I/O.
Restart and verify:
systemctl restart redis
redis-cli -a <password> ping
Response: PONG.
Step 3 — Enable phpredis for every PHP version
This is the step most operators miss. Without the phpredis C extension, the
WordPress plugin falls back to the pure-PHP predis library, which is roughly 5×
slower and undoes most of the win.
On stock EasyApache 4:
for v in 81 82 83 84; do
dnf install -y ea-php${v}-php-pecl-redis5
done
/scripts/restartsrv_apache_php_fpm
If you're running CloudLinux PHP Selector, the
extension lives in alt-php packages instead, and per-user PHP versions need it
enabled separately:
for v in 81 82 83 84; do
yum install -y alt-php${v}-pecl-redis
done
cagefsctl --force-update
cloudlinux-selector enable-extension --json --extension=redis --version=8.2
Confirm the extension is loaded for every active version:
for v in 8.1 8.2 8.3 8.4; do
echo "=== PHP $v ==="
/opt/cpanel/ea-php${v//.}/root/usr/bin/php -m | grep -i redis
done
You should see redis listed under each version. If a version is missing it, the
sites running on that version will silently fall back to predis.
Step 4 — Wire WordPress to Redis
For each WordPress account, install the Redis Object Cache plugin
(rhubarbgroup/redis-cache on WordPress.org — the free version is fine for shared
hosting; Object Cache Pro is worth it for a busy WooCommerce site but is a per-site
licence). Easiest path is WP-CLI as the cPanel user:
su - cpaneluser -c "cd ~/public_html && wp plugin install redis-cache --activate"
Then add to that account's wp-config.php, above the /* That's all, stop editing! */ line:
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_PASSWORD', 'your-redis-password' );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_PREFIX', 'cpaneluser_examplecom_' );
define( 'WP_REDIS_MAXTTL', 86400 );
define( 'WP_CACHE_KEY_SALT', 'cpaneluser_examplecom' );
The prefix is what isolates accounts. Every key Redis stores for this site will
start with cpaneluser_examplecom_, so even though every account hits the same Redis
instance, they can't read each other's cache. Use <cpanel-user>_<sanitized-domain>_
as the convention — never reuse a prefix across accounts.
Finally, enable the drop-in:
su - cpaneluser -c "cd ~/public_html && wp redis enable"
This copies object-cache.php to wp-content/, which is what WordPress actually
loads. The plugin in wp-content/plugins/ is just the management UI.
Step 5 — Verify the cache is hot
Two checks per site:
su - cpaneluser -c "cd ~/public_html && wp redis status"
Expected output: Status: Connected, Client: PhpRedis (X.Y.Z). If Client reads
Predis, jump back to step 3 — phpredis isn't loading for that PHP version.
On the server, watch hit rate:
redis-cli -a <password> INFO stats | grep -E 'keyspace_(hits|misses)'
After 15 minutes of traffic, keyspace_hits should be 5-20× keyspace_misses on a
healthy workload. A hit ratio below 50% means either the cache is being flushed too
aggressively (check maxmemory_policy) or you're undersized — bump maxmemory and
watch eviction counts drop.
Common gotchas
- PHP-FPM workers don't share Redis connections. Each worker opens its own. On a
box with tuned FPM pools running 600 workers
total, Redis sees 600 idle connections. The default
maxclients 10000is fine, butulimit -nfor the redis user needs to be at least 4096. - LSCache and Redis solve different problems. LSCache is page cache, Redis is object cache — they stack, they don't conflict. Run both for content sites where some traffic is anonymous (LSCache) and some is logged-in (Redis).
- CloudLinux LVE doesn't count Redis CPU against users. Redis runs as its own system user and lives outside CageFS, so a hot site can't be throttled by LVE for its Redis usage. This is usually fine — Redis is cheap — but worth knowing when you're tuning LVE limits.
FLUSHALLwipes every site at once. Never run it on a shared Redis. Usewp redis flushper site, which only deletes that site's prefix.- Backup tools may serialise Redis snapshots. With
appendonly noandsave "", there's nothing for JetBackup to copy, which is correct — object cache should never be in your backups.
Do I need a separate license for Redis?+
Should I use Redis or Memcached for WordPress object cache?+
Can I run one Redis per cPanel account instead of a shared instance?+
What breaks if I uninstall Redis with sites still configured?+
Does Redis work with LiteSpeed Cache?+
Next steps
- If you're running LiteSpeed, the LiteSpeed Cache plugin's built-in object cache feature can drive the same Redis instance — one plugin instead of two.
- Pair Redis with tighter PHP-FPM pools — see tuning PHP-FPM on cPanel — to reclaim the RAM Redis is now using.
- If Redis is the last piece holding back a WordPress fleet's performance, the bottleneck has probably moved to MySQL or PHP-FPM worker count, both of which benefit from CloudLinux LVE tuning.