• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 9+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Building a High-Availability, Cost-Optimized Magento 2 Stack on OVH

Building a High-Availability, Cost-Optimized Magento 2 Stack on OVH

OVHcloud Infrastructure for Magento 2: A Cost-Optimized HA Architecture

This document outlines a high-availability (HA) and cost-optimized Magento 2 architecture leveraging OVHcloud’s infrastructure. The focus is on achieving resilience and performance without incurring the premium costs often associated with managed cloud providers. We’ll detail the server roles, network configuration, database setup, caching strategies, and essential deployment practices.

Server Roles and Instance Selection

A robust Magento 2 deployment requires distinct roles for optimal performance and scalability. For cost-effectiveness on OVHcloud, we’ll utilize their “Public Cloud” instances, specifically focusing on the “General Purpose” (B2) and “RAM Optimized” (R2) series for compute and memory-intensive tasks, respectively. Storage will be handled by OVHcloud’s block storage (SSD) for performance.

  • Web Servers (2x): Handle incoming HTTP/S traffic, serve static assets, and proxy to PHP-FPM. These should be General Purpose instances (e.g., b2-7 or similar) with sufficient CPU and RAM.
  • PHP-FPM Servers (2x): Execute PHP code. These can also be General Purpose instances, potentially scaled down if web servers are powerful enough, or dedicated RAM Optimized instances (e.g., r2-7) if PHP processing is a bottleneck.
  • Database Server (1x HA Pair): A highly available MySQL setup. We’ll use two instances for replication and failover. RAM Optimized instances (e.g., r2-15 or larger) are recommended due to MySQL’s memory-intensive nature.
  • Redis Cache Server (1x HA Pair): For session storage and full-page caching. Similar to the database, a pair of instances (e.g., r2-7) configured for Sentinel-based HA is ideal.
  • Elasticsearch Server (1x HA Cluster): For search functionality. Depending on data volume, this might require dedicated instances or a cluster of smaller instances. For simplicity in this HA setup, we’ll assume a single powerful instance (e.g., r2-15) for now, with a note on scaling to a cluster.
  • Load Balancer (1x): Distributes traffic across web servers. OVHcloud’s “Load Balancer” service is a cost-effective managed option.

Network Configuration and Security

OVHcloud’s Public Cloud networking provides the necessary tools. We’ll use a private network (VPC) to isolate internal services and expose only necessary ports to the public internet via the load balancer.

  • VPC Setup: Create a dedicated Virtual Private Cloud (VPC) for all Magento-related instances.
  • Security Groups/Firewall Rules:
    • Allow HTTP (80) and HTTPS (443) from the internet to the Load Balancer.
    • Allow traffic from the Load Balancer to Web Servers on port 80.
    • Allow traffic from Web Servers to PHP-FPM Servers on port 9000.
    • Allow traffic from PHP-FPM Servers to Database Servers on port 3306.
    • Allow traffic from Web Servers and PHP-FPM Servers to Redis Servers on port 6379.
    • Allow traffic from Web Servers and PHP-FPM Servers to Elasticsearch Servers on port 9200.
    • Allow SSH (22) access only from trusted IP ranges (e.g., your office VPN or bastion host).
  • DNS: Configure A records pointing to the OVHcloud Load Balancer’s public IP address.

Web Server and PHP-FPM Setup (Nginx + PHP-FPM)

We’ll use Nginx as the web server and PHP-FPM for PHP execution. This setup will be deployed on both web and PHP-FPM server roles.

Nginx Configuration (Web Servers)

The Nginx configuration on the web servers will act as a reverse proxy to PHP-FPM and serve static assets directly. For HA, ensure both web servers have identical configurations.

/etc/nginx/sites-available/magento2

server {
    listen 80;
    server_name your_domain.com www.your_domain.com;
    root /var/www/html/public; # Magento 2 public directory

    index index.php;

    # SSL configuration (if using direct SSL on web servers, otherwise handled by LB)
    # listen 443 ssl http2;
    # ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
    # ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
    # include /etc/letsencrypt/options-ssl-nginx.conf;
    # ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ ^/static/version.*\.js$ {
        expires 1y;
        add_header Cache-Control "public";
        access_log off;
    }

    location ~ ^/static/frontend/.*\.css$ {
        expires 1y;
        add_header Cache-Control "public";
        access_log off;
    }

    location ~ ^/media/.*\.jpg$ {
        expires 1y;
        add_header Cache-Control "public";
        access_log off;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Adjust PHP version as needed
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Magento 2 specific FastCGI params
        fastcgi_param MAGE_RUN_CODE "your_store_code"; # Replace with your Magento store code
        fastcgi_param MAGE_RUN_TYPE "store"; # Or "website"
        fastcgi_param HTTP_X_FORWARDED_PROTO $scheme;
        fastcgi_param HTTP_X_FORWARDED_HOST $host;
        fastcgi_param HTTP_HOST $host;
        fastcgi_param HTTP_X_REAL_IP $remote_addr;
        fastcgi_param HTTP_CLIENT_IP $remote_addr;
    }

    location ~ /\.ht {
        deny all;
    }

    # Deny access to sensitive files
    location ~* /(composer\.json|composer\.lock|\.env|\.git|\.svn|var/cache|var/session|var/log) {
        deny all;
    }
}

PHP-FPM Configuration (PHP-FPM Servers)

Ensure PHP-FPM is configured to handle concurrent requests efficiently. The pool configuration is critical.

/etc/php/8.1/fpm/pool.d/www.conf (Example for PHP 8.1)

; Adjust pm.max_children based on server RAM and expected load.
; A common starting point is (Total RAM - 1GB) / Average Process Size.
; Monitor with `htop` and `php-fpm -t`.
pm = dynamic
pm.max_children = 100
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.process_idle_timeout = 10s

; Adjust request_terminate_timeout for long-running Magento tasks (e.g., cron)
; Magento recommends at least 600s for CLI commands. For FPM, this is less critical but can prevent timeouts.
request_terminate_timeout = 300

; Set user and group to the web server user
user = www-data
group = www-data

; Listen on a Unix socket for better performance and security
listen = /var/run/php/php8.1-fpm.sock

; Set appropriate permissions for the socket
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Increase memory limit for Magento
memory_limit = 512M

; Increase max_execution_time for CLI scripts (though FPM has request_terminate_timeout)
max_execution_time = 300

; Increase post_max_size and upload_max_filesize if large file uploads are common
post_max_size = 64M
upload_max_filesize = 64M

; Enable opcache for performance
[opcache]
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.validate_timestamps=1
opcache.enable_cli=1
opcache.file_cache_dir=/var/cache/php-fpm-opcache
opcache.file_cache_default_permissions=0666
opcache.file_cache_fallback=1
opcache.huge_code_pages=1

Important: Ensure the opcache.file_cache_dir exists and is writable by the www-data user. Create it with sudo mkdir -p /var/cache/php-fpm-opcache && sudo chown www-data:www-data /var/cache/php-fpm-opcache.

High-Availability MySQL Setup

For HA, we’ll implement MySQL replication with a primary and a replica. OVHcloud’s block storage (SSD) is crucial here for performance. We’ll use Percona XtraDB Cluster or Galera Cluster for true multi-master HA if budget allows, but for cost optimization, a primary-replica setup with automated failover is a good compromise.

Primary MySQL Server Configuration

On the primary MySQL instance:

/etc/mysql/mysql.conf.d/mysqld.cnf (Primary)

[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
relay_log = /var/log/mysql/mysql-relay-bin.log
read_only = OFF
bind-address = 0.0.0.0 ; Listen on all interfaces within the private network

# InnoDB settings for performance
innodb_buffer_pool_size = 8G ; Adjust based on instance RAM (e.g., 50% of total RAM)
innodb_log_file_size = 1G
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
innodb_file_per_table = ON

# Replication user (create this user on the primary)
# CREATE USER 'repl_user'@'%' IDENTIFIED BY 'your_replication_password';
# GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';
# FLUSH PRIVILEGES;

# GTID settings (optional but recommended for easier failover)
# gtid_mode = ON
# enforce_gtid_consistency = ON

# Other performance tuning parameters
max_connections = 500
query_cache_type = 0
query_cache_size = 0
tmp_table_size = 64M
max_heap_table_size = 64M
sort_buffer_size = 2M
join_buffer_size = 2M
read_rnd_buffer_size = 1M
thread_cache_size = 16
table_open_cache = 2000
table_definition_cache = 1000

Replica MySQL Server Configuration

On the replica MySQL instance:

/etc/mysql/mysql.conf.d/mysqld.cnf (Replica)

[mysqld]
server-id = 2
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
relay_log = /var/log/mysql/mysql-relay-bin.log
read_only = ON ; Crucial for replica
bind-address = 0.0.0.0 ; Listen on all interfaces within the private network

# InnoDB settings (should match primary)
innodb_buffer_pool_size = 8G
innodb_log_file_size = 1G
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
innodb_file_per_table = ON

# GTID settings (if enabled on primary)
# gtid_mode = ON
# enforce_gtid_consistency = ON

# Other performance tuning parameters (should match primary)
max_connections = 500
query_cache_type = 0
query_cache_size = 0
tmp_table_size = 64M
max_heap_table_size = 64M
sort_buffer_size = 2M
join_buffer_size = 2M
read_rnd_buffer_size = 1M
thread_cache_size = 16
table_open_cache = 2000
table_definition_cache = 1000

Replication Setup Steps

  1. On Primary:
    • Create replication user: CREATE USER 'repl_user'@'%' IDENTIFIED BY 'your_replication_password'; GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%'; FLUSH PRIVILEGES;
    • Get binary log position: SHOW MASTER STATUS; (Note File and Position).
  2. On Replica:
    • Stop MySQL: sudo systemctl stop mysql
    • Configure server-id and read_only=ON in mysqld.cnf.
    • Start MySQL: sudo systemctl start mysql
    • Configure replication:
      CHANGE MASTER TO
          MASTER_HOST='',
          MASTER_USER='repl_user',
          MASTER_PASSWORD='your_replication_password',
          MASTER_LOG_FILE='mysql-bin.XXXXXX', -- From SHOW MASTER STATUS
          MASTER_LOG_POS=YYYYYY; -- From SHOW MASTER STATUS
      -- If using GTID:
      -- MASTER_AUTO_POSITION=1;
    • Start replication: START SLAVE;
    • Verify replication: SHOW SLAVE STATUS\G (Ensure Slave_IO_Running: Yes and Slave_SQL_Running: Yes, and Seconds_Behind_Master is low).
  3. For HA Failover: Implement a monitoring script or use a tool like Orchestrator or MHA to detect primary failure and promote the replica. This is outside the scope of basic configuration but essential for true HA.

Redis for Caching and Sessions

Redis is critical for Magento 2 performance. We’ll set up a Redis Sentinel cluster for high availability.

Redis Master/Replica Configuration

On both Redis instances (let’s call them redis-1 and redis-2):

/etc/redis/redis.conf

# General settings
port 6379
daemonize yes
pidfile /var/run/redis/redis-server.pid
logfile /var/log/redis/redis-server.log
dir /var/lib/redis

# Network settings
bind 0.0.0.0 ; Bind to private network interface
protected-mode no ; Required if not using password and binding to 0.0.0.0

# Persistence (disable for cache-only, or configure appropriately if needed)
save ""
appendonly no

# Replication settings
replica-serve-stale-data yes
replica-read-only yes
replica-announce-ip  ; Announce this instance's IP
replica-announce-port 6379

# Sentinel configuration (on separate Sentinel instances or same as Redis)
# If running Sentinel on the same nodes, uncomment and configure these:
# sentinel monitor mymaster  6379 2
# sentinel down-after-milliseconds mymaster 6000
# sentinel failover-timeout mymaster 180000
# sentinel parallel-syncs mymaster 1
# sentinel notification-script mymaster /etc/redis/notify-script.sh
# sentinel client-reconfig-script mymaster /etc/redis/reconfig-script.sh

Redis Sentinel Configuration

Set up at least two Sentinel instances (can be on the same servers as Redis, or dedicated small instances). Let’s assume they are on redis-1 and redis-2.

/etc/redis/sentinel.conf (on both Sentinel nodes)

port 26379
daemonize yes
pidfile /var/run/redis/sentinel.pid
logfile /var/log/redis/sentinel.log
dir /var/lib/redis

# Monitor the master, require 2 sentinels to agree it's down, failover after 3 minutes
sentinel monitor mymaster  6379 2
sentinel down-after-milliseconds mymaster 6000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

# Optional: Notification and reconfig scripts for automation
# sentinel notification-script mymaster /etc/redis/notify-script.sh
# sentinel client-reconfig-script mymaster /etc/redis/reconfig-script.sh

Setup Steps:

  1. Install Redis server and Sentinel packages.
  2. Configure redis.conf on both nodes, setting replica-announce-ip correctly.
  3. Configure sentinel.conf on both nodes, pointing to the *initial* master IP.
  4. Start Redis server and Sentinel services on both nodes.
  5. On the *initial* master node (e.g., redis-1), configure it as master:
    sudo redis-cli -p 6379 replicaof no one
  6. On the *initial* replica node (e.g., redis-2), configure it as replica:
    sudo redis-cli -p 6379 replicaof  6379
  7. Verify Sentinel status: redis-cli -p 26379 SENTINEL master mymaster and redis-cli -p 26379 SENTINEL replicas mymaster.

Magento 2 Configuration (app/etc/env.php)

Update your Magento env.php to use the Sentinel master address.

<?php
return [
    'backend' => [
        'frontName' => 'admin_your_admin_path'
    ],
    'crypt' => [
        'key' => 'your_crypt_key'
    ],
    'db' => [
        'table_prefix' => '',
        'connection' => [
            'default' => [
                'host' => '',
                'dbname' => 'magento_db',
                'username' => 'magento_user',
                'password' => 'magento_password',
                'model' => 'mysql4',
                'initStatements' => 'SET NAMES utf8',
                'engine' => 'innodb',
                'active' => 1
            ],
            'slave' => [ // For read-only queries, if using separate read replicas
                'host' => '',
                'dbname' => 'magento_db',
                'username' => 'magento_user',
                'password' => 'magento_password',
                'model' => 'mysql4',
                'initStatements' => 'SET NAMES utf8',
                'engine' => 'innodb',
                'active' => 1
            ]
        ]
    ],
    'resource' => [
        'default_setup' => [
            'connection' => 'default'
        ],
        'default_read' => [
            'connection' => 'slave' // Point read operations to slave
        ]
    ],
    'cache' => [
        'frontend' => [
            'default' => [
                'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
                'options' => [
                    'server' => '', // e.g., 'mymaster'
                    'port' => '26379', // Sentinel port
                    'database' => '0',
                    'password' => '',
                    'sentinel_master_name' => '', // e.g., 'mymaster'
                    'sentinel_port' => '26379'
                ]
            ],
            'page_cache' => [
                'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
                'options' => [
                    'server' => '', // e.g., 'mymaster'
                    'port' => '26379', // Sentinel port
                    'database' => '1',
                    'password' => '',
                    'sentinel_master_name' => '', // e.g., 'mymaster'
                    'sentinel_port' => '26379'
                ]
            ]
        ]
    ]
];
?>

Note: For session storage, configure app/etc/env.php under the session key to point to Redis similarly.

Elasticsearch Integration

Elasticsearch is essential for Magento 2’s catalog search. For cost optimization, a single, well-provisioned instance is a starting point. For true HA, a multi-node cluster is required.

Elasticsearch Instance Configuration

Ensure Elasticsearch is installed and accessible on its default port (9200) within the private network. Tune JVM heap size based on instance RAM. A common recommendation is 50% of available RAM, up to a maximum of 30-32GB.

/etc/elasticsearch/jvm.options (Example)

-Xms4g
-Xmx4g

Magento 2 Configuration:

In the Magento Admin panel: Stores > Configuration > Catalog > Elasticsearch. Configure the connection details to point to your Elasticsearch instance’s private IP address.

Load Balancer Configuration (OVHcloud Managed LB)

OVHcloud’s Load Balancer service simplifies traffic distribution and SSL termination.

  • Frontend Configuration:
    • Protocol: HTTP/HTTPS
    • Port: 80/443
    • SSL Certificate: Upload your SSL certificate.
  • Backend Pool:
    • Add your two Web Server instances.
    • Protocol: HTTP
    • Port: 80
    • Health Check: Configure an HTTP health check (e.g., GET /healthz or a specific Magento endpoint) to monitor backend server availability.
  • Load Balancing Algorithm: Round Robin is a good default.

Deployment and Maintenance

Deployment Workflow

  • Code Deployment: Use a CI/CD pipeline (e.g., GitLab CI, GitHub Actions, Jenkins) to deploy code to all web and PHP-FPM servers. Ensure consistent deployment across all nodes.
  • Magento Commands: Run Magento CLI commands (e.g., setup:upgrade, cache:flush, indexer:reindex) from a single management server or via SSH to one of the web servers. For HA, ensure commands are run on all relevant nodes or that the deployment process handles this.
  • File Permissions: Maintain consistent file permissions across all web/PHP-FPM servers.

Cron Jobs

Magento cron jobs are critical. For HA, you should run cron jobs on only *one* server at a time to avoid duplicate processing. Use a distributed locking mechanism or a simple flag file on a shared volume (if available and reliable) or manually manage which server runs cron.

Example Cron Setup (on one designated server)

On the designated cron server, add entries to crontab -e:

# Magento 2 Cron Jobs
* * * * * /usr/bin/php /var/www/html/bin/magento cron:run >> /var/log/magento/cron.log 2>&1
* * * * * /usr/bin/php /var/www/html/update/cron.php >> /var/log/magento/update_cron.log 2>&1
* * * * * /usr/bin/php /var/www/html/bin/magento setup:cron:run >> /var/log/magento/setup_cron.log 2>&1

Ensure the log directory /var/log/magento exists and is writable by the web server user.

Monitoring and Alerting

Implement robust monitoring for all components:

  • Server Resources: CPU, RAM, Disk I/O, Network traffic (e.g., using Prometheus/Grafana, Zabbix, or OVHcloud’s monitoring tools).
  • Application Performance: Magento-specific metrics, error rates, response times (e.g., New Relic, Datadog, or custom logging).
  • Database: Replication status, query performance, connection counts.
  • Redis: Memory usage, connected clients, latency.
  • Elasticsearch: Cluster health, indexing speed, query latency.
  • Load Balancer: Backend health status.

Cost Optimization Strategies

OVHcloud’s pricing model allows for significant cost savings compared to hyperscalers. Key strategies include:

  • Right-Sizing Instances: Continuously monitor resource utilization and adjust instance types. Start with smaller instances and scale up as needed.
  • Reserved Instances/Savings Plans: If you have predictable long-term usage, explore OVHcloud’s options for commitment-based discounts.
  • Storage Optimization: Use SSD block storage for performance-critical databases and caches. For less critical data (e.g., logs, backups), consider cheaper storage options if available.
  • Managed Services: Leverage OVHcloud’s managed Load Balancer instead of self-hosting HAProxy/Nginx for load balancing.
  • Auto-Scaling (if applicable): While not detailed here for simplicity, consider OVHcloud’s auto-scaling capabilities for web/PHP-FPM tiers during peak traffic periods to dynamically adjust capacity and reduce costs during off-peak hours.
  • Instance Spot Market: For stateless workloads like web servers (if not requiring persistent local storage), consider OVHcloud’s spot instances for significant cost reductions, but ensure your architecture can handle potential interruptions.

Conclusion

This architecture provides a solid foundation for a high-availability Magento 2 deployment on OVHcloud, emphasizing cost-effectiveness. By carefully selecting instance types, configuring services for resilience (replication, Sentinel), and implementing robust monitoring, CTOs and VPs of Engineering can achieve a performant and reliable e-commerce platform while managing infrastructure spend effectively.

Primary Sidebar

A little about the Author

Having 9+ Years of Experience in Software Development.
Expertised in Php Development, WordPress Custom Theme Development (From scratch using underscores or Genesis Framework or using any blank theme or Premium Theme), Custom Plugin Development. Hands on Experience on 3rd Party Php Extension like Chilkat, nSoftware.

Recent Posts

  • Disaster Recovery 101: Architecting Auto-Failovers for Redis and PHP Deployments on OVH
  • How We Audited a High-Traffic WooCommerce Enterprise Stack on Google Cloud and Mitigated Race conditions during high-concurrency payment processing
  • Disaster Recovery 101: Architecting Auto-Failovers for Elasticsearch and Magento 2 Deployments on DigitalOcean
  • An Auditor’s Checklist for Securing WordPress Backends on OVH
  • Step-by-Step: Diagnosing Perl script high CPU throttling due to unoptimized regular expressions on AWS Servers

Copyright © 2026 · Vinay Vengala