Web Server Load Balancing with NGINX Plus

The Internet of Things (IoT) is already all around us – from voice‑activated devices to lights, coffee machines, and fridges – and we interact with it everyday in ways we don’t always realize. One of the biggest challenges in scaling IoT devices is device management, particularly over-the-air updates, commonly called OTA.

OTA is critical to deploying new firmware to IoT devices; without it user intervention is required, either by the consumer or by a technician visiting (possibly literally) the device in the field.

This post demonstrates how to use NGINX Open Source or NGINX Plus as an API gateway to deploy OTA updates to devices automatically. (For ease of reading, I’ll refer to NGINX throughout.) I’m using a simple API to handle the firmware versioning (so the devices know which version is the latest) and to deliver the file itself.

For the purposes of this post I’m using an ESP32‑DevkitC device, which uses WiFi for communication. The setup described below applies just as easily to most device types, depending only on the device capabilities and how the firmware is written.

Setting Up the Device

First off, if you haven’t already set up and connected the ESP32‑DevkitC to WiFi, set that up now using the tutorial from Espressif.

Once your device is rocking WiFi, the next step is to load an Arduino sketch with the updated firmware on it. To do this, you need the Arduino IDE, so if you don’t already have it:

  1. Download the Arduino IDE from the Arduino website, install it, and launch it.
  2. Navigate to Tools > Manage Libraries, and wait for the Arduino IDE to update.
  3. Search for and select esp32FOTA to install the software for updating ESP32 firmware over the air.

Using the Arduino IDE, connect to the device via either WiFi or USB and load the following sketch. Be sure to replace <domain-url> on line 11 with your domain name:

Setting Up an NGINX Instance

Now we have the board set up, it’s time to set up NGINX as an API gateway to handle versioning and delivery to device.

  1. Add the following include directive to the http block in nginx.conf:

  2. Create /etc/nginx/conf.d/api.conf with the following contents. I totally recommend that you use HTTPS and have included the directives for configuring it on lines 18–31. You need to generate a certificate and key in this case, and specify the paths to them with the ssl_certificate and ssl_certificate_key directives (lines 18–19).

    As in the sketch in the previous section, be sure to replace <domain-name> with your domain domain name on lines 8 and 11.

    The location block (lines 10–12) specifies the URI for the API (/ota) and the response that NGINX returns: status code 200 plus the data that the sketch uses, in JSON format. For now it’s hardcoded but in the next section we will make that a little more dynamic, so read on.

  3. Run these commands to validate the configuration syntax and reload the config.

    $ nginx -t
    $ nginx -s reload
  4. Before powering on the device, run a check to make sure the API is working as expected and returns the JSON you’ve loaded into the location block in api.conf:

    $ curl -X GET https://domain-url/ota
    {"type":"esp32-fota-http", "version": 100, "host": "domain-url", "port": 80, "bin": "/esp32-fota-http-100.bin"}

If you run into any issues, check the api_access.log file and retrace your steps; depending on the device you are using, HTTPS might not be supported.

Automating OTA with the NGINX JavaScript Module

Now we intro a little NGINX JavaScript magic: we are going to write JavaScript code that reads from a folder and updates the output of the API when a new OTA update file is ready for devices to load.

[Editor – This post is one of several that explore use cases for the NGINX JavaScript module. For a complete list, see Use Cases for the NGINX JavaScript Module.

This section is updated to use the js_import directive, which replaces the js_include directive in NGINX Plus R23 and later. For more information, see the reference documentation for the NGINX JavaScript module – the Example Configuration section shows the correct syntax for NGINX configuration and JavaScript files.]

  1. Follow the instructions in the NGINX Plus Admin Guide to install the NGINX JavaScript module on the NGINX host, if you haven’t already.
  2. To enable the module, edit nginx.conf and add the following load_module directives in the top‑level (“main”) context:

  3. Edit api.conf and make the following modifications. (For the purposes of this post, we instead create a copy called api_njs.conf with the modifications in it.)

    • Add this js_import directive above the server block which begins on line 4 of api.conf (see above; with the addition of this directive to api_njs.conf the server block now begins on line 6).

    • In the location /ota block, remove (or comment out) the return directive and insert the following js_content directive:

  4. Create ota.js with the following contents. Be sure to:

    • Replace </path/to/firmware> on line 5 with the path to the directory where you are storing the firmware files to be updated over the air. It must be a publicly accessible Internet location.
    • Replace <domain-url> on line 21 with your domain name.

    On line 12, we invoke the Node.js fs module‘s readFileSync function to find the latest version of the OTA update file in the directory named by otaFolder. We loop through the filenames in the directory to find the highest version number, using readFileSync to check if the next file version in the sequence exists. When a version doesn’t exist, the previous (found) version is determined to be the latest.

    For this to work, filenames must be in the following format:


    where version is 100 on the first version of the file. Increment the version number sequentially for subsequent versions (101, 102, 103, and so on).

    Note that we’re using readFileSync because at the time of writing, the NGINX JavaScript module does not support the readdir function, but only operations on individual files. [Editor – Support for readdir() was added in July 2020 in NGINX JavaScript version 0.4.2.]

  5. Run these commands to validate the configuration syntax and reload the config.

    $ nginx -t
    $ nginx -s reload
  6. Repeat the curl command from the previous section to verify the API is working as expected:

    $ curl -X GET https://domain-url/ota
    {"type":"esp32-fota-http", "version": 100, "host": "domain-url", "port": 80, "bin": "/esp32-fota-http-100.bin"}

Taking Advantage of Advanced Features

There’s a bunch of handy other features in NGINX and devices that we can use too.

For enhanced security with better authentication between device and server, many newer devices come equipped with crypto chips (like the Arduino MKR series) that allow for on‑device x.509 certificate generation.

NGINX’s advanced load balancing means you can deploy OTA update files to different server‑based regions (handy if you have country‑specific firmware) or multiple firmware servers or locations.

It’s up to each IoT device to decide when it does updates (or checks for them), so you need to keep older versions of OTA updates available. Devices are sometimes inactive or temporarily unable to get Internet access for a period of time, so make sure you keep track of your firmware versions and their dependencies.

OTA increases the longevity of device builds, as it allows you to refine and update device functionality (on the software side anyway). Of course OTA is even more important for things like security upgrades and patches, as it helps create more secure devices.

Just remember that 1 device can turn into 10 can turn into 10,000 before you know it, so it’s critical to have a solid OTA update plan in place before you scale.

To try NGINX Plus, start your free 30-day trial today or contact us to discuss your use cases.

Hero image
Managing Kubernetes Traffic with F5 NGINX: A Practical Guide

Learn how to manage Kubernetes traffic with F5 NGINX Ingress Controller and F5 NGINX Service Mesh and solve the complex challenges of running Kubernetes in production.

About The Author


Community Advocate

About F5 NGINX

F5, Inc. is the company behind NGINX, the popular open source project. We offer a suite of technologies for developing and delivering modern applications. Together with F5, our combined solution bridges the gap between NetOps and DevOps, with multi-cloud application services that span from code to customer.

Learn more at or join the conversation by following @nginx on Twitter.