Nginx Active-Passive Cluster

Categories: webserver

I have recently created my own nginx cluster. I use nginx as a reverse proxy and needed a high-availability solution. There’s already support for this in Nginx Plus, but I’m compiling my own version of the Open Source version of nginx, so I looked at their documentation as inspiration and created my own scripts. I have two servers a Primary (Node A - Active) and a Secondary (Node B - Passive), my Primary node is the where I edit/update my nginx configration and then it synchronizes the changes to my secondary node. Here’s what you need to get started


  • 3 IP addresses - Each server needs an IP address and the last is used as the floating IP address which is the one users should access
  • 2 RHEL/CentOS Servers
  • Nginx - Webserver
  • Keepalived - VRRP software
  • Rsync - Synchronization software
Install software

Install the following packages on both servers

yum install -y nginx keepalived rsync

Firewall Configuration

This should be applied to both servers. If you are serving websites on non-standard ports, then remember to open them as well

firewall-cmd --add-service=http --permanent
firewall-cmd --add-service=https --permanent
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
firewall-cmd --reload

Tweak the Linux Kernel

The Nginx and Keepalived process needs the ability to bind to a non-local IP address. This is done by creating a file in /etc/sysctl.d/

echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/90-keepalived.conf

NOTE. The above needs to be done on both servers


Node A - Primary Nginx Server -

  1. /etc/keepalived/keepalived.conf
    ! Configuration File for keepalived
    global_defs {
    	 enable_script_security					# Enable Script Security
    	 script_user YOUR-USERNAME				# Run script as this user. For security reasons, don't use root
    vrrp_script track_nginx {					# Tracking script to determine if the service is healthy
    	script "/usr/sbin/pidof nginx"			# Checks if nginx is running
    	interval 2								# Checks every 2 seconds
    	timeout 1
    vrrp_instance VI_1 {
        state MASTER							# MASTER/BACKUP
        interface ens192						# Name of interface to be used for VRRP
        virtual_router_id 51					# Router ID needs to match on all nodes
        priority 200							# A higher number has higher priority
        advert_int 1
        authentication {
          auth_type PASS
          auth_pass YOUR-PASSWORD				# Maximum 8 characters
        unicast_peer {						# IP address of our secondary node
    	virtual_ipaddress { dev ens192			# Floating/Virtual IP and the interface name to bind it to
    	track_script {
    		track_nginx							# Tracking script defined above
  1. Start Keepalived and Nginx
    systemctl enable --now keepalived.service
    systemctl enable --now nginx.service

Sync Nginx files

Now we need to detect changes in /etc/nginx and synchronize them to our secondary server. This is a two step process, first we need a script to do the actual synchronization and then we need to run it when a change has been made.

  1. Create a script called

    NodeB="" # IP address of the Node B/Secondary nginx server
    ## Check if nginx configuration is valid before synchronization
    if out=$(nginx -t 2>&1); then
    	## Sync Files
    	rsync -a --delete /etc/nginx/ $NodeB:/etc/nginx
    	## Restart Nginx
    	ssh $NodeB "systemctl restart nginx.service"
    	echo "Success"
    	echo "Failure, because $out"

    1a. Make the script executable

    chmod +x

  2. We need to create a ssh key and copy the public key to Node B

    ssh-keygen -t rsa -b 4096 -C "Nginx Primary" -f ~/.ssh/id_NodeA_rsa -N ""
    ## Copy public key to Node B
    ssh-copy-id -i ~./ssh/ [email protected]

  3. Now we need to create the monitor script. It’s made of two files placed in /etc/systemd/system/

    1. nginxFileChange.service

      Description = Starts the synchronization job from Node A to Node B
      Documentation = man:systemd.service
      ExecStart=/YOUR-PATH-TO/		# Remember to change this line to your needs

    2. nginxFileChange.path

      Description = Triggers the nginxFileChange.service which synchronizes changes
      Documentation = man:systemd.path
      PathModified=/etc/nginx/			# Path to the nginx config folder
      [Install]		# Requires at least runlevel 3 otherwise our script wont work

    3. Start the nginxFileChange.path service

      systemctl daemon-reload
      systemctl enable --now nginxFileChange.path		# Enables the file monitor check
      systemctl status nginxFileChange.service		# Shows status of the sync service


Node B - Secondary Nginx Server -

  1. /etc/keepalived/keepalived.conf
    ! Configuration File for keepalived
    global_defs {
    	 script_user root
    vrrp_script track_nginx {
    	script "/usr/bin/killall -0 nginx"
    	interval 2
    	timeout 1
    vrrp_instance VI_1 {
        state BACKUP
        interface ens192
        virtual_router_id 51
        priority 100
        advert_int 1
        authentication {
          auth_type PASS
          auth_pass YOUR-PASSWORD
        unicast_peer {
    	virtual_ipaddress { dev ens192
    	track_script {
  1. Start Keepalived and Nginx
    systemctl enable --now keepalived.service
    systemctl enable --now nginx.service


Here is a few useful commands to see if it’s working

ip address								# Shows network interfaces and IP

journalctl -r -u keepalived				# Shows nginx systemd log
journalctl -r -u nginx					# Shows keepalived systemd log

systemctl enable service-name.service		# Auto starts the service at boot
systemctl enable --now service-name.service	# Equal to systemctl enable + systemctl start 

systemctl status nginx.service			# Shows service status
systemctl status keepalived.service
systenctl status nginxFileChange.path

systemctl start nginx.service			# Start Service
systemctl start keepalived.service
systemctl start nginxFileChange.path

systemctl stop nginx.service			# Stops Service
systemctl stop keepalived.service
systemctl stop nginxFileChange.path