How to Compile Dynamic NGINX Modules

Chris Oliver

June 20, 2018

Compiling dynamic NGINX modules is something that's kind of hard to find online. Especially if you want to compile them for an existing compiled copy of NGINX. This guide is going to walk through compiling the Upload module for NGINX on Ubuntu 18.04 using nginx from their repositories.

NGINX has already been compiled for us with various different options. If you install the nginx-extras package you get NGINX compiled with a bunch of modules, but you can also use the nginx-full or just nginx packages to get less modules.

To compile our new dynamic module, we'll need to download the source code for NGINX, install any dependencies used when the Ubuntu package maintainers compiled it, and use the same flags they did.

Installing dependencies

You'll first need to install any dependencies for the NGINX modules you're compiling. For me, I needed the following:

sudo apt install libperl-dev libgeoip-dev libgd-dev

Downloading NGINX and module source code

You'll need to compile this against the same version of NGINX that's currently installed. You can find that by running nginx -v. For me, we're using 1.14.0 so we'll download the source for that version.

wget https://nginx.org/download/nginx-1.14.0.tar.gz
tar zxf nginx-1.14.0.tar.gz

Then we need to download the source for our module. We're using the Upload module which can be found on Github. We'll grab the latest zip.

wget https://github.com/fdintino/nginx-upload-module/archive/master.zip
unzip master.zip

Find the NGINX compile flags

To make our module compatible with the existing NGINX binary, we need to use the same compile flags. We can find those by running the following command:

nginx -V

# nginx version: nginx/1.14.0 (Ubuntu)
built with OpenSSL 1.1.0g  2 Nov 2017
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-mcUg8N/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-headers-more-filter --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-cache-purge --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-ndk --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-echo --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-fancyindex --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/nchan --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-lua --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/rtmp --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-uploadprogress --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-subs-filter

Now this is a LOT of flags, but we can ignore any of the ones for --add-dynamic-module because we don't need to compile those modules, just our new one. That leaves us with the following flags:

--with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-mcUg8N/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module

WARNING If you don't do this, you'll end up with an error later on when you try to load the module that looks like this: # module "/usr/lib/nginx/modules/ngx_http_upload_module.so" is not binary compatible in /etc/nginx/modules-enabled/50-mod-http-upload.conf:1

Make sure you use these same flags when compiling so you don't get this error.

Compiling the module

Now we just need to hop into the nginx source directory and compile the module. We will add the flag to compile our new dynamic module at the end after the other flags --add-dynamic-module=../nginx-upload-module-master

cd nginx-1.14.0

./configure --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-mcUg8N/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --add-dynamic-module=../nginx-upload-module-master

make modules

If you're missing any dependencies, these configure and make commands might fail. Make sure you install any dependencies that are missing and try again.

Installing the compiled module

The last step is just to move the compiled module to a place that NGINX can find and tell NGINX to load it. We'll move the compiled dynamic module to /usr/lib/nginx/modules where the other modules live and then create a config file telling NGINX to load that module.

sudo mv objs/ngx_http_upload_module.so /usr/lib/nginx/modules

echo 'load_module /usr/lib/nginx/modules/ngx_http_upload_module.so;' | sudo tee /etc/nginx/modules-enabled/50-mod-http-upload.conf

Testing the module

Last but not least we want to confirm everything is working. A quick check is we can run the following command to verify our config. If everything went well, you should get the following output.

sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

And for the real test, you can add the module's lines to your nginx server block, restart nginx, and make sure they actually work.

Conclusion

Dynamic modules are awesome. While they might be a bit harder to compile, we don't have to worry about recompiling ALL of NGINX anymore if we want to use some new plugin. Hopefully this guide helps you get modules compiled for your OS so you can continue using your repository-provided copy of NGINX.

P.S. You might enjoy following me on Twitter.


Comments