The first time an application is scaled from one server to two, something breaks, and it is almost always sessions. A login stored in a file on server A is invisible to server B, so a user whose next request lands on B is logged out. Sticky sessions paper over it and defeat the purpose of scaling. This post follows a developer fixing it properly on MassiveGRID PaaS, which is built on Virtuozzo Application Platform, formerly Jelastic, with a Redis Cluster as the shared session store.
The application is a customer portal for an insurance broker we will call Selby Insurance, a PHP back end with a Node.js real-time notification service, running on an auto-scaling application layer. The Redis Cluster package deploys a native Redis Cluster of three primaries and three replicas, sharded by hash slot, with automatic failover and the option of auto-scaling by pairs.
Why a cluster and not a single Redis node
A single Redis node would solve the session problem tonight. The developer chooses the cluster for two reasons that show up later. Availability: if the single node restarts, every user is logged out at once; in the cluster each primary has a replica that is promoted automatically, so a node failure loses nothing. Headroom: Redis is single-threaded per node, and the notification service also uses Redis as a pub/sub broker, so spreading keys across three primaries triples the CPU available. The package installs a minimum of six nodes (three primary, three replica) and can grow to twelve, always in primary-replica pairs, with automatic resharding when a pair is added.
Wiring PHP sessions to the cluster
PHP's Redis extension supports cluster mode in its session handler. The developer sets the session save handler to rediscluster and the save path to the seed nodes with the cluster's password, in the PHP configuration the platform exposes through its config editor for the application layer:
session.save_handler = rediscluster
session.save_path = "seed[]=redis-node-1:6379&seed[]=redis-node-2:6379&seed[]=redis-node-3:6379&auth=the-password&timeout=2&read_timeout=2"
Three details matter. The seed list needs only some of the nodes; the client discovers the rest through the cluster's topology. The hostnames are the nodes' internal platform hostnames, because the application layer and the Redis cluster live in the same environment group and talk over the internal network, which is free of traffic charges. And the change is applied to the whole application layer at once, then the layer is restarted sequentially so the portal never goes dark. Sessions created before the switch are lost, so the developer does it in an evening window and tells the support desk.
Wiring the Node.js service
The notification service uses ioredis, whose Cluster class takes the same seed list and handles slot discovery and MOVED redirects. Sessions for the WebSocket connections are shared with PHP by reading the same session keys, which works because both sides agree on the key prefix and serialisation. Pub/sub in Redis Cluster is broadcast to every node by default, which is fine at Selby's scale; for very high fan-out the developer notes that sharded pub/sub exists in recent Redis versions and that the client supports it.
Hash slots and the one mistake to avoid
Redis Cluster splits the key space into 16,384 hash slots, each owned by one primary. A single-key command goes to the right node transparently. A multi-key command, such as MGET across session keys or a Lua script touching several keys, only works if all the keys hash to the same slot, which they will not unless you make them. The convention is a hash tag: the part of a key in braces is what gets hashed, so session:{user42}:data and session:{user42}:flags land together. The developer audits the two places the portal used multi-key operations, adds hash tags, and the rest of the code is unchanged. Most session handlers, including PHP's, use single-key operations and need nothing.
Encryption in transit, since it is a login token store
Session identifiers are credentials, so the developer installs the Redis Encrypted Connection add-on on the cluster. It generates certificates per node, opens a TLS port (6380 by default) alongside the plain one, and stores the client files under the platform's keys directory. He switches both clients to the TLS port with the CA file, verifies with the bundled redli tool, then disables the plain port. Because everything stays on the internal network, this is defence in depth rather than a fix for exposure, and it is what the broker's security questionnaire asks for.
Sizing and cost
Sessions are small. Twenty thousand active sessions of a few kilobytes each is under 100 MiB, spread across three primaries with a replica each. The developer sets each Redis node to a low reserved count and a dynamic limit of 8 cloudlets (a cloudlet is 128 MiB of RAM plus 400 MHz of CPU); the platform's vertical scaling allocates what each node uses per hour. Six nodes idling at 2 to 3 cloudlets is roughly 15 cloudlets an hour in total, or about $37 a month at MassiveGRID's published $0.003372 per cloudlet-hour before discounts, for a session store that survives a node failure and lets the application layer scale to as many nodes as the triggers add.
| Before | After |
|---|---|
| Sessions in files on each PHP node | Sessions in a sharded Redis Cluster shared by every node |
| Sticky sessions on the balancer; uneven load | Round-robin balancing; any node serves any request |
| Scaling out logs users out | Scaling out is invisible to users |
| A node restart logs its users out | Replica promotion; no session loss |
Frequently Asked Questions
Can I use the Redis Cluster for application caching too, or should that be a separate Redis?
You can use one cluster for both, with different key prefixes and TTLs, and the cluster's capacity grows by adding pairs. Many teams prefer a separate cache instance so a cache flush or a cache-heavy workload never affects sessions; the WordPress and Magento cluster packages do exactly that.
Is data in Redis Cluster persisted to disk?
Yes. The nodes write RDB snapshots and can enable append-only-file persistence, and each primary is replicated to its replica. For a session store, losing a few seconds of writes in a crash is acceptable; for data you cannot lose, enable AOF with fsync every second on the nodes.
What if the six-node cluster is more than I need?
A single Redis node from the topology wizard is a legitimate choice for a small application, and the Redis SSL/TLS add-on works on it too. Move to the cluster when you need failover or more than one node's throughput; the client configuration change is small.
Sessions that follow the user, not the server
The Redis Cluster package deploys three sharded primaries with replicas and automatic failover on MassiveGRID PaaS, with a TLS add-on and per-cloudlet-hour billing. Free 14-day trial, no credit card.
Redis Cluster on MassiveGRID PaaS