Cyral is now GA!·Read our latest press release here
Blog

How-to: Auto Reload Nginx For Dynamic Services

Are you building services with Nginx as a proxy or load balancer? Do you need to make changes in dev or have automation in production? Read on for what we’re doing to auto reload Nginx to see those config changes immediately.

At Cyral, our core focus is being able to monitor and secure all types of data repositories. One of the fastest growing types of data repositories is not accessed through a typical database connection API, but rather via a browser such as Snowflake. To that end, we’ve worked with our customers to set up gateways for our backend services to be able to monitor and secure this traffic. We turned to Nginx for the simplicity and speed that it provides as that gateway.

Configuring Nginx to automatically reload when a change is detected speeds up the development workflow as well as accomplishing our goals to provide monitoring and security at speeds that are unnoticed by our customers. In this how-to, we’ll demonstrate configuring an openresty docker container to automatically reload Nginx when we add, remove, or modify server block configuration files.

Prerequisite Steps

For our purposes, we’re going to be testing locally using Docker and Docker Compose. If you already have Docker Desktop, you’re all set, if not, follow these instructions to install Docker.

Configure Nginx

Create an nginx.conf file in a new directory where we’ll store our scripts and Docker files to setup this project.

worker_processes 2;
error_log logs/error.log;
error_log logs/error.log notice;
error_log logs/error.log info;

events {
   worker_connections 1024;
}

http {
   include mime.types;
   default_type application/json;

   client_body_temp_path /var/run/openresty/nginx-client-body;
   proxy_temp_path /var/run/openresty/nginx-proxy;
   fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
   uwsgi_temp_path /var/run/openresty/nginx-uwsgi;
   scgi_temp_path /var/run/openresty/nginx-scgi;

   sendfile on;

   gzip on;
   gzip_proxied any;
   gzip_types *;
   gzip_vary on;

   access_log off;

   include /etc/nginx/conf.d/*.conf;
}

The relevant part of nginx.conf for this how-to is include /etc/nginx/conf.d/*.conf;. The include directive tells Nginx to import all of the configuration files found in /etc/nginx/conf.d into the http context. We’ll next write a script that will watch for file changes in /etc/nginx/conf.d/ and reload Nginx accordingly.

Create a new file called nginxReloader.sh that will use inotify-tools to listen for filesystem events and then automatically reload Nginx.

#!/bin/bash
###########

while true
do
 inotifywait --exclude .swp -e create -e modify -e delete -e move /etc/nginx/conf.d
 nginx -t
 if [ $? -eq 0 ]
 then
  echo "Detected Nginx Configuration Change"
  echo "Executing: nginx -s reload"
  nginx -s reload
 fi
done

nginxReloader.sh will continuously watch for file changes in /etc/nginx/conf.d. When a file is created, modified, deleted, or moved, the Nginx configuration will be tested with nginx -tnginx -t will test the syntax of the configuration files and ensure that all configuration files referenced are accessible. If the Nginx configuration passes testing, then Nginx will be reloaded with nginx -s reload.

Create a new file called docker-entrypoint.sh in the same directory as nginx.conf.

#!/bin/bash
###########

sh -c "nginxReloader.sh &"
exec "$@"

We’ll modify the entrypoint of the openresty Docker container so that it starts nginxReloader.sh in the background, then executes the main openresty command in the foreground by setting it to invoke docker-entrypoint.sh.

Create a new file called Dockerfile in the same working directory.

FROM openresty/openresty:buster-fat

COPY ./nginxReloader.sh /usr/local/openresty/bin/nginxReloader.sh
COPY ./docker-entrypoint.sh /usr/local/openresty/bin/docker-entrypoint.sh

RUN chmod +x /usr/local/openresty/bin/nginxReloader.sh
RUN chmod +x /usr/local/openresty/bin/docker-entrypoint.sh

RUN apt-get update && apt-get install -y --no-install-recommends apt-utils
RUN apt-get install inotify-tools -y

ENTRYPOINT [ "/usr/local/openresty/bin/docker-entrypoint.sh" ]
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]

Now we’re ready to tie everything together in the Dockerfile. Note that the above Dockerfile uses relative path to copy nginxReloader.sh and docker-entrypoint.sh to the image.

FROM openresty/openresty:buster-fat

We’ll use openresty/openresty:buster-fat as our base image:

COPY ./nginxReloader.sh /usr/local/openresty/bin/nginxReloader.sh
COPY ./docker-entrypoint.sh /usr/local/openresty/bin/docker-entrypoint.sh

RUN chmod +x /usr/local/openresty/bin/nginxReloader.sh

RUN chmod +x /usr/local/openresty/bin/docker-entrypoint.sh

These lines copy the scripts to the image and make them executable.

RUN apt-get update && apt-get install -y --no-install-recommends apt-utils
RUN apt-get install inotify-tools -y

We also need to update packages and install inotify-tools since it is a dependency for nginxReloader.sh.

ENTRYPOINT [ "/usr/local/openresty/bin/docker-entrypoint.sh" ]
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]

Lastly, we change the entrypoint of the image to docker-entrypoint.sh and set the main openresty command. The main openresty command can be found from openresty’s Dockerfile.

Now we can build the image. From the same directory the Dockerfile is in, run the following command:

docker build --no-cache -t nginx-auto-reload:local .

Now we can test the image we built, which should reload Nginx automatically for us upon a detected configuration change in /etc/nginx/conf.d/.

In order to make changes to the Docker container’s /etc/nginx/conf.d directory easier, we can create a conf.d directory on the host machine and mount it on the Docker container at /etc/nginx/conf.d.

Create a conf.d directory in the same directory as your Dockerfile.

mkdir conf.d

Create a docker-compose.yaml file with the following content in the same directory as your Dockerfile.

version: "2"

services:
 nginx-auto-reload:
  image: "nginx-auto-reload:local"
  container_name: nginx-auto-reload
  volumes:
   - ./conf.d:/etc/nginx/conf.d:ro
  ports:
   - "8080:8080"

This Docker file will overwrite the default nginx.conf of the openresty container with our nginx.conf, mount conf.d on our host machine to /etc/nginx/conf.d/ on the openresty container, and forward port 8080 on our host machine to 8080 on the openresty docker container. We’ll use port 8080 later to test a ‘Hello world’ server configuration file.

Then execute docker-compose up -d:

docker-compose up -d

Now navigate to http://localhost:8080 in your browser. You should get an empty response since we haven’t added any server block configuration files to the host machine’s conf.d directory.

Now add a file called server.conf to your conf.d directory on your host machine. Since this directory is linked to /etc/nginx/conf.d on the openresty Docker container, adding this server.conf file will trigger Nginx to reload.

server {
 listen 0.0.0.0:8080;
 server_name "localhost:8080";
 location / {
  content_by_lua_block {
   ngx.say('Hello,world!')
  }
 }
}

Navigate back to http://localhost:8080 and you will see your hello world message.

This will save development time by cutting out the time spent reloading Nginx to see changes take effect. Also, being able to automatically reload Nginx based on configuration file changes on the host machine opens up the possibility of managing Nginx with a separate service running on the host machine.

Stay Connected