The first time a PHP site is scaled to a second server, user uploads break: a file saved on node one does not exist on node two, and half the visitors see a missing image. There are two families of fix, a shared file system every node mounts, or a mirror that copies changes between local disks, and each is right for different sites. This post follows a DevOps engineer choosing the mirror for a Joomla site on MassiveGRID PaaS, which is built on Virtuozzo Application Platform, formerly Jelastic.

The site is a regional news portal for a publisher we will call Tideway Press, on the Joomla package with a few gigabytes of images uploaded by editors. Traffic has outgrown one PHP node. The File Synchronization add-on installs lsyncd on every node of an application server layer and mirrors changed files to the peers within seconds using inotify and rsync over SSH.

The problem, precisely

The platform makes adding a node easy: scale the PHP layer to two, and an NGINX balancer is added automatically with the new node behind it. Code is not the problem, because the Deployment Manager deploys to every node and stateful scaling clones the master's disk. Sessions are not the problem once they are in Redis. The problem is everything the application writes at runtime: editors' image uploads, Joomla's media folder, generated thumbnails. Node one has them, node two does not, and the balancer alternates. The engineer needs every node to see the same files, and has two ways to get there.

Two families of fix

Shared file system (Shared Storage container, GlusterFS)File mirror (File Synchronization add-on, lsyncd)
How it worksEvery node mounts the same remote directory over NFS or the Gluster clientEach node keeps a local copy; changes are detected with inotify and pushed to peers with rsync
ConsistencyImmediate; one copy of each fileEventual; propagation within seconds
Read performanceNetwork file system latency per readLocal disk speed
Single point of failureThe storage node (unless GlusterFS replicated)None; every node holds the files
Concurrent writes to one fileFile system lockingLast write wins
Extra nodes to runOne storage node, or three for GlusterNone
Best forLarge or write-heavy shared data; many nodes; files that must be strictly consistentModest upload volumes; read-heavy sites; a handful of nodes; no appetite for a storage tier

Why the mirror fits Tideway

The publisher's file set is a few gigabytes that grows slowly, written by a dozen editors and read by tens of thousands of visitors. Reads dominate by orders of magnitude, and a network file system would add latency to every image request while the mirror serves from local disk. Two or three nodes is the plan, not twenty. No two editors upload the same file at once, so last-write-wins is not a real risk. And the engineer would rather not run a storage tier for a news site. The mirror wins on all four counts, and it costs nothing beyond the nodes already running.

Installing and configuring the add-on

The add-on is installed on the PHP layer with the directories to synchronise: Joomla's images and media folders and the cache folder excluded. It installs lsyncd on every node, distributes SSH keys between them for passwordless encrypted rsync, and configures either a master-to-replicas mirror or a mesh where every node syncs to every other. Tideway uses the mesh, because uploads can arrive at any node the balancer picks. The engineer's test is the obvious one: upload an image through node one, watch it appear on node two. On the internal network it appeared in about two seconds.

The add-on's scaling hooks matter for the next step: when a horizontal scaling trigger adds a third node, it receives the current file set and joins the sync mesh automatically, so a node created at 09:00 on a busy morning has the images by the time the balancer sends it traffic. The engineer adds triggers, scale out at 65% CPU for two minutes up to four nodes, and lets the mirror follow.

What to exclude, and why

Not everything a PHP application writes should be mirrored. Caches are per node and regenerable; syncing them wastes bandwidth and can copy a stale cache over a fresh one. Session files belong in Redis, not on disk. Log files are per node by nature. Temporary upload directories are half-written files until the application moves them. The engineer's exclusion list is those four, and the rule is that the mirror carries user content and nothing that a node can rebuild for itself.

Where the mirror stops being right

The engineer wrote down the conditions for switching to shared storage, because they will arrive if the site keeps growing. If the file set reaches tens of gigabytes, a new node's initial sync becomes slow enough to matter. If editors start editing the same files concurrently, last-write-wins becomes a data-loss risk. If the layer routinely runs more than a handful of nodes, a mesh of pairwise rsync grows quadratically. At that point the Shared Storage container is the next step, and the GlusterFS Replicated Volume is the step after that when the storage itself must survive a node. This blog covers both. For a regional news site with three nodes, the mirror is the right-sized answer, and it has been running for a year.

Frequently Asked Questions

Can the add-on synchronise between two different environments?

Yes. The platform's documentation describes synchronising application servers across two separate environments, whether they run the same stack or different ones. This is how a blue and a green environment, or an active and a passive region, keep uploads aligned.

What happens when a node is down when a file is uploaded?

lsyncd on the node that received the upload retries rsync to the unreachable peer until it returns, so the file arrives once the node is back. A node restored by the platform after a failure catches up with the changes it missed.

Does the mirror work for Node.js or other stacks?

Yes. The add-on attaches to application server layers generally; the documentation lists WordPress, Drupal, Joomla, Liferay and Redmine as typical cases, and any application that writes user content to a directory benefits the same way.

Every node, the same files, no storage tier

Install the File Synchronization add-on on any application server layer on MassiveGRID PaaS and scale to several nodes with uploads mirrored in seconds, new nodes joining automatically. Free add-on, per-cloudlet-hour nodes, free 14-day trial.

File Synchronization on MassiveGRID PaaS

Further Reading