Scaling Out a Mautic Cluster with Multiple App Servers

One of the most significant challenges of self-hosting Mautic is scaling the infrastructure to meet the demand as your marketing database grows. Most first-time users of Mautic start off with the application hosted on a single server with Apache/Nginx, PHP, and MySQL all on the same node. It’s the lowest cost way to setup Mautic, being the easiest to configure without the technical implications of a distributed system. To a certain number of contacts, a single node works fine, but it requires taking the instance down for any server maintenance, which means momentarily your forms & campaigns would be non-operational.

Scaling Mautic with 1 Mautic Server (Apache/Nginx, PHP) + 1 MySQL Server

The first gambit for scaling Mautic is separating the MySQL DB server from the web & application server. The DB is the busiest part of the Mautic stack, consuming the most system resources such as CPU and RAM. If you are setting up Mautic and expect to import 20,000 contacts or more within the first year, or over 40,000 website visitors/month, it is highly recommended to set up one VPS for Mautic and a second VPS for the database. Ensure there is a robust LAN connection between the two nodes. Any cloud provider with private networking features (e.g. Virtual Private Cloud on AWS, or Private Networking in DigitalOcean or Linode) will do.

To make the MySQL server accessible on the internal network (from the Mautic server), you need to add/modify the following line in my.cnf (or the equivalent on your distribution) – where 10.0.0.2 is the MySQL server’s private IP address. Ensure that skip-networking (if exists) is commented out with a “#” symbol.

[mysqld]
bind-address 10.0.0.2
#skip-networking

Restart the MySQL service after making this change.

If you initially created the MySQL database user as ‘mysqluser’@’localhost’, login to the MySQL console (as MySQL root user) and create a new grant as follows – where 10.0.0.1 is the Mautic server’s private IP address.

mysql > grant all privileges on databasename.* to ‘mysqluser’@’10.0.0.1 identified by ‘password’;

mysql > flush privileges;

Open up any ports on the DB server firewall to allow MySQL port 3306 access from your Mautic server. If you are on a distribution such as CentOS 7 using firewalld:

firewalld-cmd --permanent --add-zone=mysql
firewalld-cmd --reload
firewalld-cmd --permanent --zone=mysql --add-source=10.0.0.1/32
firewalld-cmd --permanent --zone=mysql --add-port=3306/tcp
firewalld-cmd --reload

When installing Mautic, the database host should be specified as the LAN hostname or IP address of the MySQL server (e.g. 10.0.0.2). If Mautic was already installed with everything locally, do a mysqldump to export the data from the existing database server on localhost, transfer the .sql dump to the dedicated database server, and import it there.

Then on the Mautic server, edit the following line in app/config/local.php from the directory where Mautic is installed. And finally, clear the Symfony cache and refresh the dashboard to allow the change to take effect.

'db_host' => '10.0.0.2',

rm -rf app/cache/*

Offloading the MySQL database to a separate VPS/cloud instance can significantly make Mautic faster by cutting down CPU and RAM usage on the Mautic server. It makes scaling easier, as the application and DB servers can be resized separately. Nonetheless, this setup provides no redundancy, and it remains susceptible to downtime and possible data loss caused by unexpected server reboots and crashes.

Scaling Out Mautic with Multiple Application Servers in Cluster

Architecting Mautic for scalability in a cluster with multiple application servers is more challenging, but certainly achievable. Scaling out to multiple Mautic servers becomes necessary when you are processing many campaigns simultaneously, or you’re using the Mautic tracking code and/or landing pages on a busy website with high peak traffic.

Another benefit of scaling Mautic out to multiple application servers is you can update the environment on individual servers, including rebooting for kernel upgrades or upgrading PHP version, while keeping the marketing automation platform available to the users. The load balancer will automatically detect the instances being updated as “unhealthy” and send traffic to the remaining nodes, then put those instances back into the rotation once the maintenance is complete.

Assuming you’re deploying a Mautic cluster on the cloud, you’ll need a few extra components to make the setup work.

  • Load balancer, such as an Amazon ELB, to distribute HTTPS traffic between Mautic servers
  • MySQL server, or Amazon RDS as a database service
  • NFS server, or Amazon EFS, for shared storage
  • Redis server for shared Mautic session storage
  • Cron server for triggering Mautic cron jobs and renewing Let’s Encrypt certs

The simplest way to set up a Mautic cluster is by creating all the supporting services first. The last step should be installing Mautic on a web/application node, using the NFS service as a storage backing for the directories where persistent data is stored.

– Webroot directory (/var/www/html/ for Apache or /usr/share/nginx/html/ for Nginx)
Where the Mautic instance will be installed

– Web server config directory (/etc/httpd/conf.d/ for Apache or /etc/nginx/conf.d/ for Nginx)
Makes configuration easier as changes will be reflected across all servers

– SSL certificate directory (/etc/letsencrypt/)
If terminating SSL on the backend nodes, not the load balancer

Like the configuration earlier with 1 Mautic server + 1 MySQL server, the database host should be set to the external endpoint in the Mautic config file (the LAN IP of the database server or RDS internal endpoint). Once the first Mautic server has been installed and configured to use external storage, Redis, and DB, you can scale the cluster by simply taking a snapshot and cloning it to an additional instance at your cloud provider.

To avoid conflicts, pay attention that only one Mautic server in the cluster should running the cron jobs at any given time. It might be wise to provision additional resources to the Mautic server designated to trigger cron jobs, as segment rebuilding and email sending will spawn additional PHP workers which require additional memory. Also, only one server should run the Let’s Encrypt renewal cron job, to avoid exceeding the ACME API limits.

As long as you have a shared session store configured, the load balancer should be configured without session stickiness as it distributes the load more effectively. As the Mautic dashboard users are bounced from server-to-server, their PHP session is persisted by the Redis database. Without session stickiness or shared session storage, you will encounter login issues and see a “CSRF Error” every couple page loads while working with Mautic.

You can refer to our LAMP cluster series for general instructions on how to set up a simple cluster suitable for use with Mautic. If you want a Mautic cluster customized to meet your specific requirements, or with additional redundancy such as NFS primary/secondary clustering and MySQL master/slave replication, contact us for consulting and support with your clustered Mautic deployment.

  1. Building a Load Balanced LAMP Cluster
  2. PHP Session Storage for Load Balanced Applications
  3. Automating Let’s Encrypt HTTPS Behind a Load Balancer