Building the ngx_mzstd Zstandard module for NGINX from source
How to build ngx_mzstd from source against nginx from nginx.org's official repository, on Debian, Ubuntu, RHEL-compatible and other Linux systems, or against an nginx you compile yourself.
Summary
- Build ngx_mzstd against nginx from nginx.org’s official repository, or against an nginx you compile yourself with
--with-compat. - A dynamic NGINX module loads only into the nginx version it was built for, configured the same way.
- Building takes Rust 1.89 or newer, libclang, the PCRE2, OpenSSL and zlib headers, and your exact nginx version’s source run through
./configurewith the argumentsnginx -Vprints.makeis not needed. NGINX_BUILD_DIR=<nginx source>/objs cargo build --releasewritestarget/release/libngx_mzstd.so. The zstd C library is not needed.
The previous post installed ngx_mzstd from packages. Build it yourself when there is no package for your system, when you compile nginx yourself, or when you want a module version that has not been packaged yet.
Every command here was run on fresh containers on 22 October 2026, against nginx.org’s
nginx on Debian 13 and AlmaLinux 9, and against nginx built from source on
Ubuntu 24.04. Each one ended with nginx answering Content-Encoding: zstd.
Install nginx from nginx.org
nginx loads a dynamic module only if it was built for the same nginx version and the
same configuration. nginx.org’s packages are built with --with-compat, which makes
a separately built module match them reliably, so on Alpine, Arch Linux, openSUSE and
every other system, install nginx from
nginx.org’s official repository first.
Check that your nginx has the flag:
nginx -V 2>&1 | grep -o -- --with-compat
The module also needs nginx 1.25.4 or newer. To build nginx yourself instead, see the end of this post.
Install the build tools
You need a C toolchain, git, libclang (the Rust bindings to nginx are generated from its headers), and the headers nginx is compiled against. The zstd library is not among them.
Debian and Ubuntu:
sudo apt-get install build-essential curl git libclang-dev libpcre2-dev libssl-dev zlib1g-dev
RHEL, AlmaLinux, Rocky Linux and Oracle Linux:
sudo dnf install redhat-rpm-config gcc make git tar clang-devel pcre2-devel openssl-devel zlib-devel
redhat-rpm-config provides the compiler specs that nginx.org’s RPM builds pass to
the compiler. Without it, ./configure stops with
the invalid value in --with-ld-opt="-Wl,-z,relro -Wl,-z,now -pie".
On other systems, install the same things with the system’s package manager: a C compiler and make, git, curl, libclang, and the PCRE2, OpenSSL and zlib development headers.
Install Rust
ngx_mzstd needs Rust 1.89 or newer. Distribution packages are often older: Debian 13 ships 1.85. Install Rust with rustup as the user who will build:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
. "$HOME/.cargo/env"
Configure nginx’s source like your nginx
Download the source of exactly the version you run and configure it with the
arguments your binary was built with. Only ./configure is needed: the module reads
its output in objs/, and nothing is compiled or installed.
ver=$(nginx -v 2>&1 | sed 's|.*nginx/||')
cd ~
curl -O "https://nginx.org/download/nginx-$ver.tar.gz"
tar xzf "nginx-$ver.tar.gz"
cd "nginx-$ver"
eval "./configure $(nginx -V 2>&1 | sed -n 's/^configure arguments: //p')"
eval keeps quoted arguments such as --with-cc-opt='-O2 -g ...' in one piece. If
./configure reports a missing library, install its development package and run it
again. If you already configured this tree for
ngx_mbrotli, reuse it.
Build the module
cd ~
git clone --depth 1 --branch v0.2.3 https://github.com/M-Compressors/ngx_mzstd
cd ngx_mzstd
NGINX_BUILD_DIR="$HOME/nginx-$ver/objs" cargo build --release
v0.2.3 is the latest release at the time of writing; the
release page lists newer
ones. The module is written to target/release/libngx_mzstd.so.
Install and load it
Copy the module into nginx’s modules directory, which nginx -V shows as
--modules-path:
moddir=$(nginx -V 2>&1 | grep -o -- '--modules-path=[^ ]*' | cut -d= -f2)
sudo install -m 644 target/release/libngx_mzstd.so "$moddir/"
Load it at the very top of /etc/nginx/nginx.conf:
load_module modules/libngx_mzstd.so;
Turn compression on as in the previous post, then check and restart:
sudo nginx -t
sudo systemctl restart nginx
curl -sS -o /dev/null -D - -H 'Accept-Encoding: zstd' http://localhost/ | grep -i content-encoding
Building nginx too
To build nginx yourself instead of installing it from nginx.org, configure it with
--with-compat. This builds nginx 1.30.6 into /usr/local/nginx on Ubuntu 24.04,
with the build tools from above installed:
cd ~
curl -O https://nginx.org/download/nginx-1.30.6.tar.gz
tar xzf nginx-1.30.6.tar.gz
cd nginx-1.30.6
./configure --prefix=/usr/local/nginx --with-compat --with-threads \
--with-http_ssl_module --with-http_v2_module
make -j"$(nproc)"
sudo make install
--with-threads lets the module compress on thread pools. Build ngx_mzstd against
this tree with NGINX_BUILD_DIR="$HOME/nginx-1.30.6/objs" as above, copy
libngx_mzstd.so to /usr/local/nginx/modules/, and load it with
load_module modules/libngx_mzstd.so;.
When nginx is upgraded
A module built for one nginx version does not load into another, so after every nginx upgrade, download the new version’s source, configure it, build the module again and restart nginx. A reload is not enough: the master process keeps the old module loaded.
Errors you may see
| Error | Cause | Fix |
|---|---|---|
module "…" is not binary compatible |
The source was configured differently from the running nginx, or that nginx lacks --with-compat. |
Configure with exactly the arguments from nginx -V, with nginx from nginx.org or built with --with-compat. |
module "…" version 1030005 instead of 1030006 |
The module was built against another nginx version. | Build against the source of the version you run. |
module "ngx_http_zstd_filter_module" is already loaded |
tokers/zstd-nginx-module is still loaded; the modules have the same names. | Remove its load_module line or package. |
the invalid value in --with-ld-opt on RHEL |
The compiler specs used by nginx.org’s RPM builds are missing. | Install redhat-rpm-config. |
Unable to find libclang during cargo build |
libclang is not installed. | Install libclang-dev or clang-devel. |
Questions
Which nginx can I build ngx_mzstd for?
nginx 1.25.4 or newer, installed from nginx.org’s official repository or built yourself with --with-compat. nginx.org’s packages are built with --with-compat, which lets a separately built module match them.
Does ngx_mzstd need libzstd?
No. It contains mzstd, a Zstandard implementation in Rust, so neither the zstd library nor its headers are needed to build or run it.
What does "module is not binary compatible" mean?
The module was built against nginx source configured differently from the running binary. Configure the source with exactly the arguments nginx -V prints, and use nginx from nginx.org or an nginx built with --with-compat.
Do I have to rebuild ngx_mzstd when I upgrade nginx?
Yes. nginx refuses to load a module built for another nginx version. Build again against the new version’s source and restart nginx.
Cite this post
Mikhail Panfilov. “Building the ngx_mzstd Zstandard module for NGINX from source.” M-Compressors, 22 October 2026. https://m-compressors.github.io/2026/build-ngx-mzstd-from-source/