Configuring Craft for Load Balanced Environments

High-volume sites can run the risk of overwhelming the web server. A common approach to mediating that risk is to spread the web traffic over multiple web servers, called load balancing.

Load balancing with Craft is similar to most other PHP web apps, where you’ll likely need to adjust your setup so everything runs smoothly on multiple web servers.

PHP Sessions #

PHP sessions are file-based by default, stored locally on the server. If your load balancer distributes traffic randomly across web servers, a visitor may receive responses from more than one web server but “lose” their session limited to any one of them.

One symptom of PHP sessions not being configured correctly is experiencing constant, unexpected logouts from the Craft control panel.

There are several ways of handling this.

1. Save Sessions to a Memory Key-Value Store #

We recommend configuring PHP and Craft to store PHP sessions in an in-memory key-value store, like Redis. This option is the most performant and scalable of the solutions.

2. Save Sessions to the Database #

PHP and Craft can also be configured to store session information in the database.

3. Configure Session Affinity on the Load Balancer #

If you have control over your load balancer, one solution is to configure the load balancer’s session affinity, sometimes called “sticky sessions.” Doing this will ensure a user is always sent back to the same server their PHP session started on.

4. Save Sessions to Shared Storage #

Another solution is to save your sessions to shared, persistent storage. Point php.ini’s session.save_path to a shared folder all the web servers can access, such as an NFS mount.

Modifying sessions.save_path will stop PHP's garbage collection from running automatically. It’s up to you to clean up session files via some other mechanism like a cron job.

Assets #

Like PHP sessions, you’ll want uploaded files like images and PDFs to be available to all the web servers.

Pointing the web servers to a network share, like an NFS mount, is one option.

An easier approach, however, is to configure Craft to use a cloud-based storage service like S3 using the Amazon S3 Craft plugin.

A benefit of using the Amazon S3 plugin is trivial integration with Cloudfront, a CDN that caches and distributes your images globally.

There are also Craft plugins for Google Cloud Storage, DigitalOcean Spaces, and Azure Blob Storage.

Caching #

Craft’s general-purpose data caches are stored in the file system by default, so you will run into the same issues as PHP’s file-based sessions and assets. However, the solutions are similar; you can configure Craft to store its caches in the databaseRedis or Memcached.

securityKey Config Setting #

Every web server should have the same Craft securityKey config setting set to ensure all web servers can decrypt any encrypted database contents. If you are using .env, this can be done with a SECURITY_KEY PHP constant.

storage Folder #

Craft’s storage folder is used for files like database backups, logs, and custom site logos. You should point that folder to a shared storage location, like an NFS mount, using the CRAFT_STORAGE_PATH PHP constant so all web servers can access it.

cpresources Folder #

Craft will generate the control panel resources it needs on demand, so using common storage for this folder is not required. Be sure that all missing file requests are routed to index.php, however, otherwise some of these may 404.

Database #

It’s common to have multiple web servers interact with a single, high-performance database server, but database traffic may also be load-balanced.

One way to load balance database traffic is to utilize read/write splitting. This involves having multiple database instances where you can configure Craft to send all read queries to one (or more) database instances, and all write queries to a different database instance. The write queries will eventually propagate any changes to all of the read instances behind the scenes.

You’ll want to allocate database servers to suit the site’s operations—most commonly one for writing and several for reading, since the majority of traffic fetches details from the database.

Logging #

By default, Craft will log to a file in the storage path. Since there are many servers in a load balanced environment and potentially lots of logging, we recommend collecting these logs in a single place. If you’re using a container-based system such as AWS ECS or Kubernetes, it’s best to configure Craft to log straight to STDOUT and STDERR using yii2-streamlog.

Health Checks #

Health checks are a key part of load-balanced applications. A health check is designed to ensure all your app’s necessary services are available before the load balancer sends traffic to the server. Depending on your setup, these services may include a database or required environment variables, for example.

Because load-balanced web servers are managed automatically, it’s essential that a health check verifies the minimum requirements for each to operate with Craft before handling traffic. Every Craft site will require its database at minimum.

Since Craft’s plugin architecture depends on Craft, it’s best to perform the health check outside of Craft, but using the same environment variables/database configuration that Craft is using.

Load balancers run health checks frequently, so it’s important to keep checks simple. For example, this sample script ensures a database connection exists and outputs success or error:

<?php

try {
    (new PDO(
        'mysql:host=' . getenv('DB_SERVER'),
        getenv('DB_USER'),
        getenv('DB_PASSWORD')
    ))->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    echo 'success';
} catch (PDOException $e) {
    error_log($e->getMessage());

    echo 'error';
}