Latest Nginx and OpenSSL on Raspberry Pi

Note: to skip the reasoning and cut to the chase, see Installation below.

Why Nginx on Pi?

Although Apache still powers the majority of sites overall, its popularity is declining. Conversely, Nginx (pronounced engine-x) enjoys a fast-growing share of the market; almost two-thirds of the world's top 10,000 websites run on Nginx[1].
In large part, the key to Nginx's success is its event-driven design, meaning it scales very predictably, enabling sites to support massive amounts of traffic. It's extremely fast and flexible, with a reasonable configuration syntax.
If you need a web server, reverse proxy or software load balancer, you would be daft not to at least consider using Nginx.

On average, every minute one of the top 10 million websites starts to use Nginx

Nginx is so efficient that its performance is pretty good even when running on modest hardware like a Raspberry Pi 2 or 3.
I'm not suggesting you can run a major website from a Raspberry Pi, but the faster models are surprisingly capable as small web servers - especially when you consider their frugal power consumption of around 1.5W idle and 2-4W when busy[2].

Why not just apt-get it?

There are good reasons to use the latest and greatest version of Nginx. Some advanced features like proxying websocket connections (which I need for the web front ends of my Ubiquiti Edgerouter Lite and Unifi wifi controller) have only matured recently. I serve both ECDSA and RSA certificates for my sites, to get the best of both worlds in terms of performance and compatibility with older clients. Such 'hybrid' certificates are only supported in Nginx >= 1.11.

If you want up-to-date cryptography for your web server, such as the ChaCha20-Poly1305 cipher suites for better performance on mobile devices, then you also need an up-to-date version of the OpenSSL cryptography toolkit for Nginx to use. There are good reasons to do so:

The new cipher suites are fast. As Adam Langley described, ChaCha20-Poly1305 is three times faster than AES-128-GCM on mobile devices. Spending less time on decryption means faster page rendering and better battery life. Although the cipher part of TLS may not be the biggest source of battery consumption, spending fewer CPU cycles on encryption saves battery life, especially on large files.[3]

New features like these take considerable time to filter down to the offical, packaged versions available for Raspbian Jessie. At the time of writing, using sudo apt-get install nginx would install version 1.6.2 - a couple of years behind the latest mainline version, 1.13.1.

Finally, just in case you're still not convinced, building from source allows you to control which optional modules are included or not for your specific Nginx build.
In other words, if you'll be needing modules like ngx_http_stub_status_module (which I use for monitoring the load on my Nginx servers via my dashboard), then you'll need to compile from source anyway.

What's the catch?

Compiling software from source isn't (usually) as easy as installing a package and requires a bit of extra work to set up things like a service definition for systemd, so that your web server runs automatically on startup following a reboot.

You need patience, too: it takes quite a long time to compile OpenSSL and Nginx, especially on older / less powerful Pi devices. Making a cup of tea helps with this one.

There is an argument for using older, package-managed versions of software rather than self-built versions simply because it's easy to update to a new version using your package manager (e.g. apt) when updates are made available in your chosen repositories. If it's easier to maintain your software in the future, you'll be more likely to apply any important security fixes, goes the theory.

We can mitigate these problems by using a script to make installation and upgrades almost as easy as using a packaged version.

Luckily for us, we don't have to write anything ourselves, as there's a great Nginx Build Script by Matthew Vance (based on an original script by Matt Wilcox), which automates most of the process for us.

How the script works

Here's a summary of what it does:

  1. Installs/updates tools essential to the build
  2. Downloads the required source code packages, comparing the SHA256 checksum values of each download against known good ones, to make sure the download wasn't corrupted somehow:
  • PCRE (Perl Compatible Regular Expressions), to enable and optimise the use of regular expressions in Nginx configurations
  • zlib, enabling ngx_http_gzip_module, for gzip encoding of responses
  • OpenSSL for cryptographic functions i.e. SSL/TLS
  • Nginx itself, of course
  1. Double-checks that the Nginx and OpenSSL sources are genuine downloads, by checking their code-signing signatures
  2. Unpacks the compressed download files
  3. Renames the current /etc/nginx folder (if present) to make a dated backup copy
  4. Creates various subfolders for Nginx caches in /var/cache
  5. Sets up the nginx user and group (if they don't already exist) ready for Nginx to run as
  6. Tests the local gcc compiler capabilities and optionally enables a special performance boost, if possible
  7. Builds Nginx, which in turn builds OpenSSL, with various optional modules enabled/disabled
  8. Restores the backed up config from the previous install (if available)
  9. Creates a systemd service file, to enable Nginx to run as a service from startup

By customising the script, we can fine-tune which optional modules are included/excluded from our own version of Nginx.
The defaults are quite sensible for general web server and reverse proxy uses, but you can always run the script again with different options - see Upgrades for how to do this.

Installation

I've reproduced these installation instructions from the nginx-build Github page, with some explanatory comments:

# create a working folder where the source will be unpacked and compiled
sudo mkdir /usr/local/src/nginx/

# switch to the working folder we just created
cd /usr/local/src/nginx/

# download the latest version of the script
sudo curl -L https://raw.githubusercontent.com/MatthewVance/nginx-build/master/build-nginx.sh -o build_nginx.sh

This next step is really important. Please take the time to check the contents of the script to make sure you're happy with it. You don't have to be an expert to compare the steps listed above explaining what the script does (in how the script works), against the copy you've just downloaded.

You're going to make this script executable and then effectively give it root privileges by running it with sudo, so it would be madness not to check it before you run it.

# review downloaded code before executing
cat build_nginx.sh

# make the script executable
sudo chmod +x build_nginx.sh

# run it
sudo ./build_nginx.sh

It will take quite a while for the script to do its work. You can watch along, or make yourself a well-earned cuppa at this point.

When finished, you can start Nginx once using sudo nginx, but you probably really want to make Nginx run automatically on startup, so use these commands:

# start nginx service
sudo systemctl start nginx

# make it start automatically when the system starts
sudo systemctl enable nginx

Upgrades

When a new version of Nginx and/or OpenSSL are released, the script is updated shortly afterwards to use the new version(s). You might also decide to re-run the script after customising the list of optional modules in the compilation settings in the script.

Whatever the reason, the steps involved in upgrading are very similar to the initial installation:

# move to the working folder
cd /usr/local/src/nginx/

# remove the old version of the script
# skip this step if you're just adding/removing Nginx modules
sudo rm /usr/local/src/nginx/build_nginx.sh

# download the latest version of the script
# (don't bother if you're just re-running to add/remove modules)
sudo curl -L https://raw.githubusercontent.com/MatthewVance/nginx-build/master/build-nginx.sh -o build_nginx.sh

# IMPORTANT! review downloaded code before executing
cat build_nginx.sh

# make the downloaded script executable
sudo chmod +x build_nginx.sh

# run the script
sudo ./build_nginx.sh

# restart the service
sudo systemctl restart nginx

# confirm the new version number is reported (if applicable)
nginx -v

# check the full version details, including OpenSSL version and compilation options
nginx -V

Conclusion

Rolling your own Nginx - including the latest stable OpenSSL - is well worth doing. Any concerns over complex compilation syntax or difficulty keeping your custom copy up-to-date, can be addressed using a script specially written to make building and maintaining your own Nginx easy peasy, lemon squeezy!


  1. https://w3techs.com/blog/entry/nginx_reaches_33_3_percent_web_server_market_share_while_apache_falls_below_50_percent ↩︎

  2. https://www.pidramble.com/wiki/benchmarks/power-consumption ↩︎

  3. https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/ ↩︎