Skip to content
EuroVPSEuroVPS

Wiring PHP to Valkey Sentinel, and proving the failover works

A replicated cache only helps if PHP follows the promotion. How to set up Sentinel, connect WordPress and Laravel, pick an eviction policy, test it.

By EuroVPS Engineering

5 min read
Wiring PHP to Valkey Sentinel, and proving the failover works

Plenty of sites run a replicated Redis or Valkey cache and still go down when the primary dies, because the application is pointed at one fixed address. The replicas are there, Sentinel promotes one, and PHP keeps knocking on the door of the dead server. Here is how to build the cache so the failover actually reaches the application, and how to prove it before you need it.

Why the cache is part of your availability

An object cache stores computed results: options, menus, sessions, query results, rendered fragments. On a busy WordPress, Magento or Laravel site it absorbs most of the database's read load. Lose it, and every request goes back to the database at once. The database was never sized for that, so the site slows to a crawl within minutes. A single cache instance is therefore a single point of failure, even though it holds nothing you cannot rebuild.

The shape: one primary, two replicas, three Sentinels

Run three cache nodes on three separate hosts: one primary and two replicas. Next to each, run a Sentinel process. The Sentinels watch the primary, agree among themselves when it is gone, promote a replica, and answer the question every client needs answered: where is the primary now?

On each replica:

replicaof <primary-address> 6379
masterauth <secret>
requirepass <secret>

And on each of the three Sentinels:

sentinel monitor cache <primary-address> 6379 2
sentinel auth-pass cache <secret>
sentinel down-after-milliseconds cache 5000
sentinel failover-timeout cache 30000
sentinel parallel-syncs cache 1

The 2 in the monitor line is the quorum: two of three Sentinels must agree the primary is down before a failover starts. That is why there are three of them, on three different hosts.

down-after-milliseconds is the trade-off between speed and caution. We keep it short. An unnecessary failover costs a few seconds of cache misses; a long wait with a dead primary costs a crawling database.

The step people skip: clients that ask Sentinel

A client configured with the primary's address will never learn about a promotion. The application must connect through Sentinel, by service name, and ask for the current primary.

With phpredis directly:

$sentinel = new RedisSentinel(['host' => '<sentinel-1>', 'port' => 26379]);
[$host, $port] = $sentinel->getMasterAddrByName('cache');
$redis = new Redis();
$redis->connect($host, (int) $port);
$redis->auth('<secret>');

Production code should try each Sentinel in turn, since any one of them may be on the host that just failed.

For WordPress, the widely used Redis Object Cache drop-in supports Sentinel natively in wp-config.php:

define('WP_REDIS_CLIENT', 'phpredis');
define('WP_REDIS_SENTINEL', 'cache');
define('WP_REDIS_SERVERS', [
    'tcp://<sentinel-1>:26379',
    'tcp://<sentinel-2>:26379',
    'tcp://<sentinel-3>:26379',
]);
define('WP_REDIS_PASSWORD', '<secret>');

Laravel, through Predis, takes a list of Sentinels and two options in config/database.php:

'redis' => [
    'client' => 'predis',
    'default' => [
        'tcp://<sentinel-1>:26379',
        'tcp://<sentinel-2>:26379',
        'tcp://<sentinel-3>:26379',
        'options' => ['replication' => 'sentinel', 'service' => 'cache'],
    ],
],

With this in place, a promotion needs no configuration change and no restart of PHP.

Memory: cap it and choose who gets evicted

A cache that grows until the kernel's OOM killer intervenes has failed in the worst possible way. Set maxmemory to a fixed share of the node's RAM, leaving room for replication buffers and the fork used for persistence, and choose an eviction policy deliberately:

  • allkeys-lru for a pure cache. When memory is full, the least recently used key goes, whatever it is. This is right for object caches.
  • volatile-lru if the instance also holds keys without an expiry that must never be evicted, such as sessions. Only keys with a TTL are candidates.

Better still, keep sessions and object cache apart. A deploy that flushes the object cache should never log every customer out. On a shared cluster, give each site its own logical database at minimum; give large or critical sites their own instance, so one site's flush or runaway key growth cannot empty its neighbours' caches.

Prove it: kill the primary on purpose

A failover you have not watched is a hope. Before handover, we run this with the application connected and traffic flowing:

# 1. Who is primary right now?
valkey-cli -p 26379 SENTINEL get-master-addr-by-name cache

# 2. Watch the site from outside, timestamped
while true; do
  printf '%s ' "$(date +%T.%N)"
  curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://www.example.com/
  sleep 0.5
done | tee cache-failover.log

# 3. On the primary's host, stop the cache service
systemctl stop valkey

# 4. Watch the Sentinel log for +switch-master, then ask again
valkey-cli -p 26379 SENTINEL get-master-addr-by-name cache

(With Redis, the commands are the same under redis-cli.)

Write down three things: how long until +switch-master appears, how long until page timings return to normal, and whether any request failed rather than merely slowed. A healthy build promotes in a few seconds, and PHP follows without anyone touching it. Then start the old primary again and confirm it rejoins as a replica of the new one, not as a second primary.

For a gentler rehearsal that does not stop a service, SENTINEL FAILOVER cache forces a promotion on demand. Useful for testing the client side; less useful for testing detection, which is the part that matters on a bad night.

Your checklist

  • Three cache nodes and three Sentinels on three separate hosts, quorum of two.
  • Authentication on, and the cache reachable only from the web tier that uses it.
  • Every application connecting through Sentinel by service name, not to a fixed address.
  • maxmemory set and an eviction policy chosen for what the instance holds.
  • Sessions separated from the object cache; big sites isolated from small ones.
  • A recorded failover test with the application connected, including the old primary rejoining.

If you would like us to wire up or test your cache layer, we are happy to.

Talk to an engineer

Published 22 October 2026.

All field notes