Install from Source

This article describes how to install Nginx by compiling it from its source-code on a Ubuntu Server.

This is a little bit more complicated then a default install, but allows us to use the most current version and install additional 3rd-party modules like brotli compression, new ciphers with a more recent OpenSSL version and ngx_cache_purge.

Note

All the following steps are also available as downloadable shell script.

Software Package Repository

The Nginx project releases its own ready-made Ubuntu software packages.

The ‘mainline’ releases contain the latest stable code as recommended for use by the project.

Add these software repositories to the systems package list:

$ sudo -s
 echo "deb http://nginx.org/packages/mainline/ubuntu/ `lsb_release -sc` nginx" \
    > /etc/apt/sources.list.d/nginx.org-mainline.list
$ echo "deb-src http://nginx.org/packages/mainline/ubuntu/ `lsb_release -sc` nginx" \
    >> /etc/apt/sources.list.d/nginx.org-mainline.list
$ exit

All software packages released by the project are signed with a GPG key.

Add the signing key to the systems trusted keyring:

$ wget -O - http://nginx.org/keys/nginx_signing.key | sudo apt-key add -

For working with the source code later, the signing key needs to be added to the personal trusted keyring too:

$ wget -O - http://nginx.org/keys/nginx_signing.key | \
    gpg --no-default-keyring --keyring ~/.gnupg/trustedkeys.gpg --import -

Update the systems packages list:

$ sudo apt update

Install required Software

Get all the stuff needed for building nginx and modules:

$ sudo apt install autoconf build-essential devscripts git \
    libgd-dev libgeoip-dev libpcre3 libpcre3-dev libxslt1-dev libxml2-dev \
    python-dev python2.7 unzip zlib1g-dev
$ sudo apt build-dep nginx
$ sudo apt-get install

Set Version Numbers

Set the Nginx Changes version number:

$ export NGX_VERSION=1.17.0

Set the OpenSSL version:

$ export OPENSSL_VERSION=1.1.1c

Set the ngx_cache_purge module version:

$ export NCP_VERSION=2.3

Set the Headers-More module version:

$ export NHM_VERSION=0.33

Set the ngx-fancyindex module version:

$ export FANCYINDEX_VERSION=0.4.3

Get the Source Code

Prepare source code directory:

$ sudo mkdir -p /usr/local/src/nginx
$ sudo chown $USER /usr/local/src/nginx
$ sudo chmod u+rwx /usr/local/src/nginx

Source Code for Nginx

Get the Nginx package source code:

$ cd /usr/local/src/nginx
$ apt source nginx

Modules:

$ apt source nginx-module-geoip nginx-module-image-filter nginx-module-xslt

Source Code for OpenSSL

Get and verify OpenSSL source:

$ cd /usr/local/src/nginx
$ wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz
$ wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz.asc
$ gpg2 --verify openssl-${OPENSSL_VERSION}.tar.gz.asc
$ tar -xzvf openssl-${OPENSSL_VERSION}.tar.gz
$ rm openssl-${OPENSSL_VERSION}.tar.gz

Build and install a standalone OpenSSL binary (optional):

$ cd /usr/local/src/nginx/openssl-${OPENSSL_VERSION}/
$ sudo mkdir -p /opt/openssl-${OPENSSL_VERSION}
$ ./config --prefix=/opt/openssl-${OPENSSL_VERSION} no-shared
$ make clean
$ make depend
$ make
$ make test
$ sudo make install_sw
$ /opt/openssl-${OPENSSL_VERSION}/bin/openssl version

Source Code for Brötli

Get and install Brötli source:

$ cd /usr/local/src/nginx
$ git clone https://github.com/google/brotli.git
$ cd brotli
$ sudo python setup.py install

Get Brötli Nginx module source:

$ cd /usr/local/src/nginx
$ git clone https://github.com/google/ngx_brotli.git

Make sure that the git submodule has been checked out:

cd /usr/local/src/nginx/ngx_brotli
git submodule update --init

Source Code for Cache Purge Module

Download the source code for the ngx_cache_purge module:

$ cd /usr/local/src/nginx
$ wget -O ngx_cache_purge-${NCP_VERSION}.zip \
    https://github.com/FRiCKLE/ngx_cache_purge/archive/${NCP_VERSION}.zip
$ unzip ngx_cache_purge-${NCP_VERSION}.zip

Source Code for Headers More Module

Download the source code for the headers-more-nginx-module:

$ cd /usr/local/src/nginx
$ wget -O ngx_headers_more-${NHM_VERSION}.tar.gz \
    https://github.com/openresty/headers-more-nginx-module/archive/v${NHM_VERSION}.tar.gz
$ tar -xzf ngx_headers_more-${NHM_VERSION}.tar.gz

Source Code for the Fancy Index Module

Download the source code for the ngx-fancyindex module:

$ cd /usr/local/src/nginx
$ wget -O ngx-fancyindex-${FANCYINDEX_VERSION}.tar.gz \
    https://github.com/aperezdc/ngx-fancyindex/archive/v${FANCYINDEX_VERSION}.tar.gz
$ tar -xzf ngx-fancyindex-${FANCYINDEX_VERSION}.tar.gz

Package Configuration

Open the file /usr/local/src/nginx/nginx-${NGX_VERSION}/debian/rules with your editor.

Add or modify lines of the ./configure commands in the Debian rules file. You have the following choices:

Remove unneeded modules by removing lines:

--with-http_flv_module \
--with-http_geoip_module=dynamic \
--with-http_image_filter_module \
--with-http_mp4_module \
--with-http_random_index_module \
--with-mail \
--with-stream \

Add additional modules:

--with-http_xslt_module \
--add-module=/usr/local/src/nginx/headers-more-nginx-module-${NHM_VERSION} \
--add-module=/usr/local/src/nginx/nginx-fancyindex-${FANCYINDEX_VERSION} \
--add-module=/usr/local/src/nginx/ngx_brotli \
--add-module=/usr/local/src/nginx/ngx_cache_purge-${NCP_VERSION} \

Set location of the OpenSSL source libraries instead of the system default:

--with-openssl=/usr/local/src/nginx/openssl-${OPENSSL_VERSION} \

Don’t forget to escape preceding lines with a backslash \.

/usr/local/src/nginx/nginx-${NGX_VERSION}/debian/rules
#!/usr/bin/make -f

#export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS=hardening=+all,-pie
export DEB_CFLAGS_MAINT_APPEND=-Wp,-D_FORTIFY_SOURCE=2 -fPIC
export DEB_LDFLAGS_MAINT_APPEND=-Wl,--as-needed -pie
#DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/buildflags.mk

PKGS = nginx nginx-dbg

BUILDDIR_nginx = $(CURDIR)/debian/build-nginx
BUILDDIR_nginx_debug = $(CURDIR)/debian/build-nginx-debug
INSTALLDIR = $(CURDIR)/debian/nginx
BASEDIR = $(CURDIR)

ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
	NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
	ifeq (${NUMJOBS}, 0)
		NUMJOBS = 1
	endif
else
	NUMJOBS = 1
endif

DO_PKGS = $(PKGS)

config.env.%:
	dh_testdir
	mkdir -p $(BUILDDIR_$*)
	cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/
	cp -Pa $(CURDIR)/conf $(BUILDDIR_$*)/
	cp -Pa $(CURDIR)/configure $(BUILDDIR_$*)/
	cp -Pa $(CURDIR)/contrib $(BUILDDIR_$*)/
	cp -Pa $(CURDIR)/man $(BUILDDIR_$*)/
	cp -Pa $(CURDIR)/src $(BUILDDIR_$*)/
	touch $@

config.status.nginx: config.env.nginx
	cd $(BUILDDIR_nginx) && \
	CFLAGS="" ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx \
		--modules-path=/usr/lib/nginx/modules \
		--conf-path=/etc/nginx/nginx.conf \
		--error-log-path=/var/log/nginx/error.log \
		--http-log-path=/var/log/nginx/access.log \
		--pid-path=/var/run/nginx.pid \
		--lock-path=/var/run/nginx.lock \
		--http-client-body-temp-path=/var/cache/nginx/client_temp \
		--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
		--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
		--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
		--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
		--user=nginx --group=nginx \
		--with-compat --with-file-aio --with-threads \
		--with-http_addition_module \
		--with-http_auth_request_module \
		--with-http_dav_module \
		--with-http_geoip_module \
		--with-http_gunzip_module \
		--with-http_gzip_static_module \
		--with-http_realip_module \
		--with-http_secure_link_module \
		--with-http_slice_module \
		--with-http_ssl_module \
		--with-http_stub_status_module \
		--with-http_sub_module \
		--with-http_v2_module \
		--with-http_xslt_module \
		--add-module=${SRC_DIR}/headers-more-nginx-module-${NHM_VERSION} \
		--add-module=${SRC_DIR}/ngx-fancyindex-${FANCYINDEX_VERSION} \
		--add-module=${SRC_DIR}/ngx_brotli \
		--add-module=${SRC_DIR}/ngx_cache_purge-${NCP_VERSION} \
		--with-openssl=${SRC_DIR}/openssl-${OPENSSL_VERSION} \
		--with-openssl-opt='enable-ec_nistp_64_gcc_128 enable-tls1_3' \
		--with-cc-opt="$(CFLAGS)" --with-ld-opt="$(LDFLAGS)"
	touch $@

config.status.nginx_debug: config.env.nginx_debug
	cd $(BUILDDIR_nginx_debug) && \
	CFLAGS="" ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx \
		--modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf \
		--error-log-path=/var/log/nginx/error.log \
		--http-log-path=/var/log/nginx/access.log \
		--pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock \
		--http-client-body-temp-path=/var/cache/nginx/client_temp \
		--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
		--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
		--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
		--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
		--user=nginx --group=nginx \
		--with-compat --with-file-aio --with-threads \
		--with-http_addition_module \
		--with-http_auth_request_module \
		--with-http_dav_module \
		--with-http_geoip_module \
		--with-http_gunzip_module \
		--with-http_gzip_static_module \
		--with-http_realip_module \
		--with-http_secure_link_module \
		--with-http_slice_module \
		--with-http_ssl_module \
		--with-http_stub_status_module \
		--with-http_sub_module \
		--with-http_v2_module \
		--with-http_xslt_module \
		--add-module=${SRC_DIR}/headers-more-nginx-module-${NHM_VERSION} \
		--add-module=${SRC_DIR}/ngx-fancyindex-${FANCYINDEX_VERSION} \
		--add-module=${SRC_DIR}/ngx_brotli \
		--add-module=${SRC_DIR}/ngx_cache_purge-${NCP_VERSION} \
		--with-openssl=${SRC_DIR}/openssl-${OPENSSL_VERSION} \
		--with-openssl-opt='enable-ec_nistp_64_gcc_128 enable-tls1_3' \
		--with-cc-opt="$(CFLAGS) -Wno-missing-field-initializers" --with-ld-opt="$(LDFLAGS)" \
		--with-debug
	touch $@

build-arch.%: config.status.%
	dh_testdir
	dh_prep
	$(MAKE) -j$(NUMJOBS) -C $(BUILDDIR_$*) build

build-arch: build-arch.nginx build-arch.nginx_debug
	dh_testdir
	touch $@

build-dbg.%: install
	dh_testdir
	dh_strip --dbg-package=nginx-dbg

build-dbg: build-dbg.nginx
	dh_testdir
	touch $@

build-indep:
	dh_testdir
	touch $@

build: build-arch build-indep
	dh_testdir
	touch $@

clean:
	dh_testdir
	dh_testroot
	dh_clean
	rm -f $(CURDIR)/objs
	rm -rf $(CURDIR)/debian/build-*
	rm -f $(CURDIR)/debian/*.init
	find $(CURDIR) -maxdepth 1 -size 0 -delete

post-build:
	mv $(BUILDDIR_nginx_debug)/objs/nginx $(BUILDDIR_nginx_debug)/objs/nginx-debug
	ln -s $(BUILDDIR_nginx)/objs $(CURDIR)/objs
	cp $(BUILDDIR_nginx)/objs/nginx.8 $(BUILDDIR_nginx)/objs/nginx-debug.8

install:
	dh_testdir
	dh_testroot
	dh_prep
	dh_installdirs
	dh_install
	mkdir -p $(INSTALLDIR)/usr/lib/nginx/modules
	mkdir -p $(INSTALLDIR)/usr/share/doc/nginx
	install -m 644 debian/CHANGES $(INSTALLDIR)/usr/share/doc/nginx/changelog
	install -m 644 debian/nginx.default.conf $(INSTALLDIR)/etc/nginx/conf.d/default.conf
	ln -s /usr/lib/nginx/modules $(INSTALLDIR)/etc/nginx/modules

binary-indep: build post-build install
	dh_testdir
	dh_testroot
	dh_installman -i -pnginx
	dh_installdebconf
	sed -e 's/%%PROVIDES%%/nginx/g' \
		-e 's/%%DEFAULTSTART%%/2 3 4 5/g' \
		-e 's/%%DEFAULTSTOP%%/0 1 6/g' \
		< debian/nginx.init.in > debian/nginx.init
	dh_installinit -i -pnginx --no-restart-on-upgrade --no-start --name=nginx
	dh_systemd_enable -pnginx --name=nginx nginx.service
	sed -e 's/%%PROVIDES%%/nginx-debug/g' \
		-e 's/%%DEFAULTSTART%%//g' \
		-e 's/%%DEFAULTSTOP%%/0 1 2 3 4 5 6/g' \
		< debian/nginx.init.in > debian/nginx-debug.init
	dh_installinit -i -pnginx --no-restart-on-upgrade --no-start --noscripts --name=nginx-debug
	dh_systemd_enable -pnginx --name=nginx-debug --no-enable nginx-debug.service
	dh_installlogrotate -i -pnginx --name=nginx

binary-arch: install build-dbg
	dh_testdir
	dh_testroot
	dh_installchangelogs -a
	dh_installdocs -a
	dh_lintian -a
	dh_link -aA
	dh_compress -a
	dh_perl -a
	dh_fixperms -a
	dh_installdeb -a
	dh_shlibdeps -a
	dh_gencontrol -a
	dh_md5sums -a
	dh_builddeb $(foreach p,$(DO_PKGS),-p$(p))

binary: binary-indep binary-arch

.PHONY: build clean binary-indep binary-arch binary install

Increase Package Version

Ubuntu will always remember, that our package was not installed from the official package source, and will therefore always offer to “upgrade” our package to the “newest version”, which is essentially the same version we already have. By increasing the version number of our package, we don’t get bothered with update notfications:

$ cd /usr/local/src/nginx/nginx-${NGX_VERSION}
$ dch

An editor opens where the package changes can be entered.

The scheme used by Ubuntu software packages is:

<Upstream Version>-<Debian Version>~Ubuntu<Ubuntu Package Version>

A new version number and your name are already pre-filled:

nginx (1.13.1-1~xenialubuntu1) UNRELEASED; urgency=medium
.
  * Change server name reported in HTTP responses
  * Build against OpenSSL 1.1.0c
  * Added dynamic XSLT module
  * Added 3rd-party brotli compression module
  * Added 3rd-party cache purge module
  * Added 3rd-party Google PageSpeed module
  * Changed mail module to dynamic

After saving and closing the file the customized package source is ready for building.

Building the Software

In case you want to restart from a clean build:

$ cd /usr/local/src/nginx/nginx-${NGX_VERSION}
$ dpkg-buildpackage -rfakeroot -Tclean

Build the Nginx package as follows:

$ cd /usr/local/src/nginx/nginx-${NGX_VERSION}
$ dpkg-buildpackage -rfakeroot -uc -b

Depending on your options the building process might take anything form five minutes to over a half-hour to complete.

Package Installation

Install the package(s):

$ cd /usr/local/src/nginx
$ sudo dpkg --install nginx_${NGX_VERSION}-1~xenialubuntu1_amd64.deb

Nginx is installed and started as system service nginx running as user nginx.

Configuration files are found in the /etc/nginx directory.

Prevent future releases to automatically overwrite our customized packages:

$ sudo apt-mark hold nginx

Test

Show version number and available modules:

$ nginx -V
nginx version: nginx/1.13.1
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
built with OpenSSL 1.1.0f  25 May 2017
TLS SNI support enabled