Building NGINX from source on Ubuntu lets you control modules, compile flags, and install paths so the web server matches your workload. It is a solid fit when you need third-party modules, want early access to new protocol features, or must compile against a specific OpenSSL build.
This guide covers build dependencies, downloading the source, configuring and compiling NGINX, creating a systemd service, and verifying the final installation.
Source builds do not receive automatic security updates from Ubuntu. Plan to monitor NGINX security advisories and rebuild when new releases or patches are published.
These steps target Ubuntu 26.04 LTS, 24.04 LTS, and 22.04 LTS. The build workflow is the same across these releases, but HTTP/3 support depends on your OpenSSL version: Ubuntu 26.04 ships OpenSSL 3.5.3, while 24.04 and 22.04 ship OpenSSL 3.0.x and use the compatibility layer for QUIC.
If you prefer repository-managed updates, follow our NGINX installation guide for Ubuntu instead.
Install Build Dependencies
Update Package Lists
Refresh your package index so you install the latest build dependencies:
sudo apt update
Install Required Packages
Install the core build tools and libraries NGINX needs to compile:
sudo apt install build-essential curl libpcre2-dev libssl-dev zlib1g-dev libgd-dev
Here is what each package provides:
build-essential: GCC, make, and headers required to compile software.curl: Downloads source tarballs without needing a browser.libpcre2-dev: PCRE2 regular expressions used for rewrites and location matching.libssl-dev: TLS support for HTTPS and HTTP/2.zlib1g-dev: Compression support for gzip and related modules.libgd-dev: Required for the image filter module when building it as a dynamic module.
Optional Libraries for XSLT Support
Install these only if you plan to enable the XSLT module:
sudo apt install libxml2-dev libxslt1-dev
The XSLT module is enabled with --with-http_xslt_module or --with-http_xslt_module=dynamic during configuration.
The legacy GeoIP module relies on the deprecated libGeoIP library and is not covered in this guide; if you need GeoIP2, use the third-party module from MaxMind instead.
Download the NGINX Source Code
Create a Build Directory
Keep source builds in a user-owned directory to avoid permission issues:
mkdir -p ~/src/nginx
cd ~/src/nginx
Choose a Release and Download the Tarball
Pick a version from the official NGINX download page. Mainline is the recommended channel for feature updates and fixes, while stable changes less frequently.
Set a version variable, then download the matching tarball:
NGINX_VERSION=1.29.4
curl -fLO --progress-bar https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
The -fLO flags fail fast on HTTP errors, follow redirects, and write the file with its original name.
#=#=# ##O#-# ##O=# # #=#=-# # -#O#- # # # 2.5% ##### 7.5% ########### 16.2% ######################## 33.8% ################################################# 68.8% ######################################################################## 100.0%
Extract and Enter the Source Tree
Extract the archive and move into the source directory:
tar -xzf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}
Configure the Build
The ./configure script detects system libraries and generates the Makefile based on the modules and paths you choose.
| Category | Option | What It Does |
|---|---|---|
| Paths | --prefix=/path | Sets the base install prefix for default paths. |
--sbin-path=/path | Sets the NGINX binary location. | |
--conf-path=/path | Sets the main nginx.conf path. | |
--modules-path=/path | Sets the directory for dynamic modules. | |
| Libraries | --with-openssl=/path | Builds NGINX against OpenSSL sources in that directory. |
| Core HTTP | --with-http_ssl_module | Adds TLS support. |
--with-http_v2_module | Enables HTTP/2 support. | |
--with-http_v3_module | Enables HTTP/3 support with a QUIC-capable TLS stack. | |
--with-http_gzip_static_module | Serves pre-compressed .gz assets. | |
--with-http_realip_module | Restores client IPs behind proxies and CDNs. | |
--with-http_stub_status_module | Adds a basic status endpoint. | |
| Dynamic Modules | --with-http_image_filter_module=dynamic | Builds the image filter module as a loadable .so. |
--with-http_xslt_module=dynamic | Builds the XSLT module as a loadable .so. | |
--add-module=/path | Compiles a third-party module statically. | |
--add-dynamic-module=/path | Compiles a third-party module as a dynamic module. | |
| Performance | --with-threads | Enables thread pools for blocking disk I/O. |
--with-file-aio | Enables asynchronous file I/O. | |
--with-pcre-jit | Enables PCRE2 JIT for faster regex matching. | |
--with-pcre=/path | Builds PCRE2 from source at the given path. | |
| Debugging | --with-debug | Builds with debug logging enabled. |
For the full option list, run ./configure --help in the source directory.
Configure NGINX for Ubuntu Paths
This example keeps configuration in /etc/nginx, logs in /var/log/nginx, caches in /var/cache/nginx, and installs the binary in /usr/sbin. It also builds the image filter module as a dynamic module so you can load it only when needed.
./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=/run/nginx.pid \
--lock-path=/var/lock/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=www-data \
--group=www-data \
--with-threads \
--with-file-aio \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_image_filter_module=dynamic \
--with-pcre-jit
If you do not need the image filter module, remove --with-http_image_filter_module=dynamic and omit libgd-dev from the dependency list.
checking for PCRE2 library ... found checking for OpenSSL library ... found checking for zlib library ... found checking for GD library ... found checking for GD WebP support ... found creating objs/Makefile Configuration summary + using threads + using system PCRE2 library + using system OpenSSL library + using system zlib library
Load the Image Filter Module
If you enabled the image filter module, add a load_module line at the top of /etc/nginx/nginx.conf before the events block:
load_module /usr/lib/nginx/ngx_http_image_filter_module.so;
events {
worker_connections 1024;
}
http {
# Your HTTP configuration
}
Compile and Install NGINX
Compile the source. The -j$(nproc) flag uses all available CPU cores to speed up the build.
make -j$(nproc)
make -f objs/Makefile cc -c -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I src/core -I src/event -I src/event/modules -I src/event/quic -I src/os/unix -I objs \ -o objs/src/core/nginx.o \ src/core/nginx.c
Install the compiled binary and configuration files to the paths you set during configuration:
sudo make install
Create Runtime Directories
The temporary cache paths you configured are not created automatically. Create them before starting NGINX:
sudo mkdir -p /var/cache/nginx/client_temp \
/var/cache/nginx/proxy_temp \
/var/cache/nginx/fastcgi_temp \
/var/cache/nginx/uwsgi_temp \
/var/cache/nginx/scgi_temp
Set ownership so the www-data worker user can write temporary files:
sudo chown -R www-data:www-data /var/cache/nginx
Create a systemd Service
Use systemd to manage NGINX like any other Ubuntu service:
Create the service unit:
sudo nano /etc/systemd/system/nginx.service
Use the unit below, adjusting the binary path if you installed NGINX elsewhere:
[Unit]
Description=NGINX web server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/usr/sbin/nginx -s quit
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Reload systemd and start the service:
sudo systemctl daemon-reload
sudo systemctl start nginx
Enable NGINX at boot so it starts automatically after reboots:
sudo systemctl enable nginx
Verify the Installation
Check Configuration Syntax
Validate your configuration before serving traffic:
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Confirm the Build Flags
Check the compiled version and configure arguments:
nginx -V
nginx version: nginx/1.29.4 built by gcc 15.2.0 (Ubuntu 15.2.0-11ubuntu1) built with OpenSSL 3.5.3 16 Sep 2025 TLS SNI support enabled configure arguments: --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=/run/nginx.pid --lock-path=/var/lock/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=www-data --group=www-data --with-threads --with-file-aio --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-http_realip_module --with-http_stub_status_module --with-http_image_filter_module=dynamic --with-pcre-jit
Test the Default Page
Request the default page locally to confirm the service is responding:
curl -I -s http://127.0.0.1
HTTP/1.1 200 OK Server: nginx/1.29.4 Date: Sat, 17 Jan 2026 01:41:00 GMT Content-Type: text/html Content-Length: 615 Last-Modified: Sat, 17 Jan 2026 01:29:47 GMT Connection: keep-alive ETag: "696ae60b-267" Accept-Ranges: bytes
If you are testing from another machine, ensure port 80 is allowed through your firewall. See our UFW firewall guide for Ubuntu if you need help opening HTTP access.
HTTP/3 and QUIC Notes
Add --with-http_v3_module to your configure command when you want HTTP/3 support. NGINX recommends OpenSSL 3.5.1 or higher for QUIC support; see the official QUIC documentation for current guidance.
On Ubuntu 26.04, the configure check reports the QUIC API as available. On Ubuntu 24.04 and 22.04, you will see the OpenSSL compatibility checks instead:
checking for OpenSSL QUIC API ... not found checking for BoringSSL-like QUIC API ... not found checking for OpenSSL QUIC compatibility ... found
The compatibility layer allows HTTP/3 builds but does not support early data. If you need full QUIC feature support, build NGINX with OpenSSL 3.5.1+ using --with-openssl=/path or a QUIC-capable TLS stack such as BoringSSL or QuicTLS.
Upgrade Source-Built NGINX
Create an Update Script
The script below checks your installed version, compares it with the latest mainline release on nginx.org, and rebuilds only when an update is needed.
Create the script file:
cat <<'EOF' > ~/nginx-update.sh
#!/usr/bin/env bash
set -euo pipefail
# Settings
BUILD_DIR="$HOME/src/nginx"
NGINX_BIN="/usr/sbin/nginx"
DOWNLOAD_PAGE="https://nginx.org/en/download.html"
# Safety checks
if [ "${EUID}" -eq 0 ]; then
echo "Run this script as a regular user. It uses sudo only for install steps."
exit 1
fi
for cmd in curl tar make gcc; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: $cmd is required but not installed."
echo "Run: sudo apt install build-essential curl"
exit 1
fi
done
if [ ! -x "$NGINX_BIN" ]; then
echo "Error: NGINX binary not found at $NGINX_BIN."
echo "Update NGINX_BIN in this script to match your install."
exit 1
fi
# Version detection
CURRENT_VERSION="$($NGINX_BIN -v 2>&1 | sed -n "s/^nginx version: nginx\\///p")"
LATEST_VERSION="$(curl -fsSL "$DOWNLOAD_PAGE" | grep -oE "nginx-[0-9]+\\.[0-9]+\\.[0-9]+" | head -n 1 | cut -d- -f2)"
if [ -z "$LATEST_VERSION" ]; then
echo "Error: Could not detect the latest NGINX version."
exit 1
fi
echo "Current version: $CURRENT_VERSION"
echo "Latest version: $LATEST_VERSION"
if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ]; then
echo "Already up to date."
exit 0
fi
CONFIGURE_ARGS="$($NGINX_BIN -V 2>&1 | sed -n "s/^configure arguments: //p")"
if [ -z "$CONFIGURE_ARGS" ]; then
echo "Error: Could not read configure arguments from nginx -V."
exit 1
fi
# Download and build
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
rm -rf "nginx-${LATEST_VERSION}" "nginx-${LATEST_VERSION}.tar.gz"
curl -fLO --progress-bar "https://nginx.org/download/nginx-${LATEST_VERSION}.tar.gz"
tar -xzf "nginx-${LATEST_VERSION}.tar.gz"
cd "nginx-${LATEST_VERSION}"
./configure $CONFIGURE_ARGS
make -j"$(nproc)"
sudo make install
# Verify and reload
sudo nginx -t
sudo systemctl reload nginx || sudo nginx -s reload
echo "Updated to $($NGINX_BIN -v 2>&1 | sed -n "s/^nginx version: nginx\\///p")"
EOF
Make the script executable, then run it:
chmod +x ~/nginx-update.sh
~/nginx-update.sh
Current version: 1.29.4 Latest version: 1.29.4 Already up to date.
If your saved configure arguments include quoted values (for example, custom
--with-cc-optor--with-ld-optflags), setCONFIGURE_ARGSmanually in the script. The script tracks the latest mainline release; for the stable channel, replace theLATEST_VERSIONline with the stable version from nginx.org.
Troubleshooting Common Issues
Missing Development Libraries
If ./configure reports missing libraries, install the required packages and rerun the configure step:
sudo apt install libpcre2-dev libssl-dev zlib1g-dev libgd-dev libxml2-dev libxslt1-dev
OpenSSL and HTTP/3 Mismatches
Verify your build flags with nginx -V and confirm you added --with-http_v3_module if HTTP/3 is required. For full QUIC support, build against OpenSSL 3.5.1+ or another QUIC-capable TLS stack and consult the NGINX QUIC documentation for the current requirements.
Configuration Test Failures
If NGINX fails to start due to config errors, rerun sudo nginx -t and fix the line number reported in the output, then retest.
Port Already in Use
If NGINX cannot bind to port 80 or 443, identify the process already using the port:
sudo ss -tlnp | grep :80
Stop the conflicting service or change the NGINX listen port. See our port change guide for NGINX for a step-by-step walkthrough.
Remove Source-Built NGINX
Stop and disable the service:
sudo systemctl stop nginx
sudo systemctl disable nginx
Remove the systemd unit and reload systemd:
sudo rm -f /etc/systemd/system/nginx.service
sudo systemctl daemon-reload
Remove the NGINX binary and dynamic module directory:
sudo rm -f /usr/sbin/nginx
sudo rm -rf /usr/lib/nginx
Removing configuration and logs deletes all site configs and history. Back up anything you want to keep before running the next commands.
sudo rm -rf /etc/nginx
sudo rm -rf /var/log/nginx /var/cache/nginx
Remove the build directory from your home folder:
rm -rf ~/src/nginx
If you installed build dependencies only for this build and do not need them elsewhere, you can remove them:
sudo apt remove --autoremove build-essential libpcre2-dev libssl-dev zlib1g-dev libgd-dev libxml2-dev libxslt1-dev
Next Steps
Customize /etc/nginx/nginx.conf for your sites, then consider enabling gzip compression using our NGINX gzip guide.