Installing and Configuring an Enterprise WordPress Environment on RHEL 9: Complete High-Availability Architecture
Prerequisites and Initial Setup
This guide assumes a RHEL 9 minimal installation with root or sudo privileges. We will deploy a highly available WordPress environment leveraging Nginx as the web server, MariaDB for the database, and Redis for object caching. For high availability, we’ll configure two web servers behind a load balancer (HAProxy in this example) and a primary/replica MariaDB cluster. Ensure all servers have static IP addresses and can resolve each other by hostname. Firewall rules will be adjusted as needed.
On each WordPress web server (node1 and node2):
- Update the system:
sudo dnf update -y sudo dnf upgrade -y
- Install necessary packages:
sudo dnf install -y epel-release sudo dnf install -y nginx php php-fpm php-mysqlnd php-gd php-xml php-mbstring php-json php-opcache redis
On the database server (db1 and db2):
- Install MariaDB packages:
sudo dnf install -y mariadb-server mariadb-client
On the load balancer server (lb1):
- Install HAProxy:
sudo dnf install -y haproxy
MariaDB Galera Cluster Setup
We’ll set up a two-node MariaDB Galera Cluster for synchronous replication. This ensures data consistency across both database servers.
On db1 (Primary):
- Configure MariaDB for Galera:
# /etc/my.cnf.d/galera.cnf [galera] wsrep_on=ON wsrep_provider=/usr/lib64/galera-4/libgalera_smm.so wsrep_cluster_name="wp_cluster" wsrep_cluster_address="gcomm://192.168.1.101,192.168.1.102" # IPs of db1 and db2 wsrep_node_name="db1" wsrep_node_address="192.168.1.101" # IP of db1 binlog_format=ROW default_storage_engine=InnoDB innodb_autoinc_lock_mode=2 innodb_flush_log_at_trx_commit=0 innodb_buffer_pool_size=1G
On db2 (Replica):
- Configure MariaDB for Galera (similar to db1, adjust
wsrep_node_nameandwsrep_node_address):
# /etc/my.cnf.d/galera.cnf [galera] wsrep_on=ON wsrep_provider=/usr/lib64/galera-4/libgalera_smm.so wsrep_cluster_name="wp_cluster" wsrep_cluster_address="gcomm://192.168.1.101,192.168.1.102" # IPs of db1 and db2 wsrep_node_name="db2" wsrep_node_address="192.168.1.102" # IP of db2 binlog_format=ROW default_storage_engine=InnoDB innodb_autoinc_lock_mode=2 innodb_flush_log_at_trx_commit=0 innodb_buffer_pool_size=1G
On db1:
- Start MariaDB and initialize the cluster:
sudo systemctl start mariadb sudo galera_new_cluster
On db2:
- Start MariaDB (it will join the existing cluster):
sudo systemctl start mariadb
After starting both, verify cluster status:
sudo mariadb -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
The output should show wsrep_cluster_size as 2.
Secure your MariaDB installation:
sudo mariadb-secure-installation
Create a WordPress database and user. Execute this on either db1 or db2, as changes will replicate.
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'wp_user'@'%' IDENTIFIED BY 'your_strong_password'; GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'%'; FLUSH PRIVILEGES;
Nginx and PHP-FPM Configuration
Configure Nginx to serve WordPress and PHP-FPM for processing PHP requests. This configuration will be identical on both web servers (node1 and node2).
Create a WordPress directory:
sudo mkdir -p /var/www/wordpress
Configure PHP-FPM pool for Nginx. Edit /etc/php-fpm.d/www.conf. Ensure the user and group are set to nginx (or the user Nginx runs as).
; /etc/php-fpm.d/www.conf [www] user = nginx group = nginx listen = /run/php-fpm/www.sock listen.owner = nginx listen.group = nginx listen.mode = 0660 pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 2 pm.max_spare_servers = 5 pm.process_idle_timeout = 10s request_terminate_timeout = 120s php_admin_value[memory_limit] = 256M php_admin_value[upload_max_filesize] = 64M php_admin_value[post_max_size] = 64M php_admin_value[max_execution_time] = 300
Start and enable PHP-FPM and Nginx:
sudo systemctl start php-fpm sudo systemctl enable php-fpm sudo systemctl start nginx sudo systemctl enable nginx
Create an Nginx virtual host configuration for WordPress. Create /etc/nginx/conf.d/wordpress.conf:
# /etc/nginx/conf.d/wordpress.conf
server {
listen 80;
server_name your_domain.com www.your_domain.com; # Replace with your domain
root /var/www/wordpress;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 300; # Increase timeout for long operations
}
location ~ /\.ht {
deny all;
}
# Caching for static assets (optional but recommended)
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
# Deny access to sensitive files
location ~* (wp-config\.php|wp-settings\.php|wp-load\.php|wp-cron\.php) {
deny all;
}
}
Test Nginx configuration and reload:
sudo nginx -t sudo systemctl reload nginx
WordPress Installation
Download and extract the latest WordPress version on one of the web servers (e.g., node1). The files will be copied to node2 later.
cd /tmp wget https://wordpress.org/latest.tar.gz tar -xzf latest.tar.gz sudo mv wordpress/* /var/www/wordpress/
Set correct ownership and permissions:
sudo chown -R nginx:nginx /var/www/wordpress
sudo find /var/www/wordpress/ -type d -exec chmod 755 {} \;
sudo find /var/www/wordpress/ -type f -exec chmod 644 {} \;
Copy WordPress files to the second web server (node2):
# On node1 sudo rsync -avz /var/www/wordpress/ node2_ip:/var/www/wordpress/
On both web servers (node1 and node2), create the wp-config.php file. Copy wp-config-sample.php and edit it.
# On node1 and node2 sudo cp /var/www/wordpress/wp-config-sample.php /var/www/wordpress/wp-config.php sudo nano /var/www/wordpress/wp-config.php
Update the database details in wp-config.php. Use the database name, user, and password created earlier. For high availability, point to the Galera cluster’s virtual IP or a DNS entry that resolves to the load balancer for the database.
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress_db' );
/** MySQL database username */
define( 'DB_USER', 'wp_user' );
/** MySQL database password */
define( 'DB_PASSWORD', 'your_strong_password' );
/** MySQL hostname */
// For Galera, use a virtual IP or DNS that points to your HAProxy DB load balancer
// If not using a DB load balancer, you might need to configure WordPress to connect to one node
// and rely on Galera's automatic failover, but a dedicated DB load balancer is recommended.
define( 'DB_HOST', '192.168.1.200' ); // Example: Virtual IP for DB HAProxy
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The Database Collate type. Get this in your phpMyAdmin or by running "SHOW COLLATION;". */
define( 'DB_COLLATE', '' );
/**#@-*/
/**
* WordPress Database Table prefix.
*
* You can have multiple installations in one database if you give each a unique
* prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';
/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*/
define( 'WP_DEBUG', false );
/* Add Redis Object Cache */
define( 'WP_REDIS_HOST', '127.0.0.1' ); // Assuming Redis is on the same server for simplicity
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_PASSWORD', '' ); // Set if you have a password
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_CACHE_KEY_PREFIX', 'wp_' );
define( 'WP_CACHE', true );
/* That's all, stop editing! Happy publishing. */
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ );
}
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
Generate unique security keys and salts. You can get them from https://api.wordpress.org/secret-key/1.1/salt/. Replace the placeholder section in wp-config.php with the generated keys.
Redis Object Caching
Install and configure Redis for object caching on each web server. This significantly improves WordPress performance by reducing database load.
On node1 and node2:
- Start and enable Redis:
sudo systemctl start redis sudo systemctl enable redis
Install the Redis Object Cache plugin for WordPress. Download the latest release from GitHub or use WP-CLI.
# On node1 and node2 cd /var/www/wordpress/wp-content/plugins/ wget https://github.com/rhubarbgroup/redis-cache/archive/refs/tags/2.1.5.tar.gz # Replace with latest version tar -xzf 2.1.5.tar.gz mv redis-cache-2.1.5 redis-cache rm 2.1.5.tar.gz chown -R nginx:nginx redis-cache
Activate the plugin via the WordPress admin dashboard or WP-CLI:
# On node1 (or any server with WP-CLI installed) cd /var/www/wordpress/ wp plugin activate redis-cache --allow-root
Ensure the Redis configuration in wp-config.php (shown in the previous section) is correct. You should see the Redis object cache status in the WordPress admin area under “Settings” -> “Redis”.
HAProxy Load Balancer Configuration
Configure HAProxy to distribute traffic between the two Nginx web servers and optionally between the two MariaDB servers.
Edit the HAProxy configuration file: /etc/haproxy/haproxy.cfg.
# /etc/haproxy/haproxy.cfg
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend http_frontend
bind *:80
default_backend http_backend
backend http_backend
balance roundrobin
option httpchk GET /
server node1 192.168.1.11:80 check
server node2 192.168.1.12:80 check
# Optional: Database Load Balancing (if using a separate HAProxy for DB)
# If you are using a dedicated DB load balancer, configure it here.
# For simplicity, we'll assume WordPress is configured to use a virtual IP
# that points to a DB load balancer or a single DB node with Galera handling failover.
# If you want HAProxy to load balance DB traffic directly:
# mode tcp
# option tcp-check
# server db1 192.168.1.101:3306 check
# server db2 192.168.1.102:3306 check
Start and enable HAProxy:
sudo systemctl start haproxy sudo systemctl enable haproxy
Ensure firewall rules allow traffic on port 80 (HTTP) and potentially port 3306 (MySQL) if HAProxy is load balancing the database.
# On lb1 sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --reload # On web servers (node1, node2) - allow traffic from HAProxy sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.200/32" port port="80" protocol="tcp" accept' # Replace with LB IP sudo firewall-cmd --reload # On DB servers (db1, db2) - allow traffic from HAProxy (if DB LB is used) # sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.200/32" port port="3306" protocol="tcp" accept' # Replace with LB IP # sudo firewall-cmd --reload
Final WordPress Configuration and Security
Access your WordPress site via the IP address or domain name configured in HAProxy. Complete the WordPress installation wizard.
Security Best Practices:
- HTTPS: Implement SSL/TLS certificates (e.g., Let’s Encrypt) on your web servers and configure Nginx to use HTTPS. Update HAProxy to forward HTTPS traffic or handle SSL termination.
- File Permissions: Regularly audit and maintain strict file permissions. The
nginx:nginxownership and 755/644 permissions are a good starting point. - WordPress Updates: Keep WordPress core, themes, and plugins updated to patch security vulnerabilities.
- Security Plugins: Consider using security plugins like Wordfence or Sucuri for additional protection.
- Database Security: Regularly back up your MariaDB cluster. Use strong, unique passwords for your database user. Restrict access to the database server from trusted IPs only.
- PHP Hardening: Further harden PHP settings in
php.ini(e.g., disable dangerous functions, set appropriate resource limits). - Nginx Hardening: Implement rate limiting, block common exploits, and configure security headers in Nginx.
- Fail2Ban: Install and configure Fail2Ban on all servers to protect against brute-force attacks.
This setup provides a robust, scalable, and highly available WordPress environment. Monitoring is crucial for maintaining performance and uptime. Implement monitoring tools (e.g., Prometheus, Grafana, Nagios) to track server health, resource utilization, and application performance.
Leave a Reply
You must be logged in to post a comment.