HTTP/3 support, upstream release control, and third-party modules are the usual reasons to build NGINX from source on Debian instead of taking the default package. When you build NGINX from source on Debian, you decide which modules are compiled in, keep the upstream stable branch within reach, and avoid waiting on Debian’s package cadence when you need newer web-server features.
The build path here pulls the current stable tarball from nginx.org and compiles it on Debian 13 (trixie), Debian 12 (bookworm), and Debian 11 (bullseye). It replaces the upstream sample configuration with a Debian-friendly conf.d layout, adds a systemd unit, and leaves you with a reusable update-nginx-source helper for later upgrades. If you would rather stay with packaged builds, use install NGINX on Debian for the default APT package or install NGINX mainline on Debian for the upstream repository track.
The source build and the
--with-http_v3_moduleflag completed on Debian 13, 12, and 11. Debian 13 is still the best fit here if you also want the newer OpenSSL 3.5.x feature set alongside the same stable NGINX branch.
Build NGINX from Source on Debian
Debian desktop installs may already include some compiler tools, but server, cloud, and minimal images usually do not. Start by refreshing package metadata so the build dependencies and OpenSSL headers come from the current Debian repositories.
Update Debian Before Building NGINX from Source
Refresh APT and apply any pending upgrades before you install the compiler toolchain.
sudo apt update && sudo apt upgrade -y
These commands use
sudo. If your account does not have sudo access yet, follow the guide on how to add a user to sudoers on Debian before you continue.
Install NGINX Source Build Dependencies on Debian 13
Debian 13 uses the PCRE2 development package, so install that variant before you download the source tree.
sudo apt install build-essential libpcre2-dev libssl-dev zlib1g-dev libgd-dev curl -y
Install NGINX Source Build Dependencies on Debian 12 and Debian 11
Debian 12 and Debian 11 still use the older PCRE development package name, so the dependency list changes slightly on those releases.
sudo apt install build-essential libpcre3-dev libssl-dev zlib1g-dev libgd-dev curl -y
These packages cover the compiler toolchain, regular-expression support, OpenSSL headers, compression libraries, image-filter support, and the curl download client used in the next step.
Download the Current Stable NGINX Source Tarball on Debian
Keep using the same terminal session for the next two blocks. The first command resolves the current stable tarball name from the nginx.org download page so you do not have to hardcode a version in the article or in your shell history.
mkdir -p "$HOME/nginx-source"
cd "$HOME/nginx-source"
NGINX_TARBALL="$(curl -fsSL https://nginx.org/en/download.html | tr '<' '\n' | sed -n '/h4>Stable version/,/\/table>/p' | grep -Eo 'nginx-[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' | head -n1)"
printf '%s\n' "$NGINX_TARBALL"
nginx-1.28.2.tar.gz
The exact tarball name changes when nginx.org publishes a new stable point release. Once the variable prints the tarball you want, download and extract it in the same shell session.
curl -fLO "https://nginx.org/download/$NGINX_TARBALL"
tar -xzf "$NGINX_TARBALL"
cd "${NGINX_TARBALL%.tar.gz}"
test -f auto/configure && printf 'source tree ready: %s\n' "$PWD"
source tree ready: /home/your-user/nginx-source/nginx-1.28.2
Configure the NGINX Source Build on Debian
These configure flags match the validated Debian build path from the SSH hosts. The binary ends up in /usr/sbin, the main configuration file lives at /etc/nginx/nginx.conf, and dynamic modules are written under /etc/nginx/modules.
./configure \
--prefix=/var/www/html \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--with-pcre \
--lock-path=/var/lock/nginx.lock \
--pid-path=/var/run/nginx.pid \
--with-http_ssl_module \
--with-http_image_filter_module=dynamic \
--modules-path=/etc/nginx/modules \
--with-http_v2_module \
--with-http_v3_module \
--with-stream=dynamic \
--with-http_addition_module \
--with-http_mp4_module
Relevant output includes:
Configuration summary + using system PCRE2 library + using system OpenSSL library + using system zlib library nginx path prefix: "/var/www/html" nginx binary file: "/usr/sbin/nginx" nginx modules path: "/etc/nginx/modules" nginx configuration file: "/etc/nginx/nginx.conf" nginx pid file: "/var/run/nginx.pid" nginx error log file: "/var/log/nginx/error.log" nginx http access log file: "/var/log/nginx/access.log"
Debian 12 and Debian 11 report using system PCRE library instead of using system PCRE2 library. The rest of the validated summary stayed consistent across Debian 13, 12, and 11.
Compile and Install NGINX on Debian
The -j"$(nproc)" form tells make to use the available CPU cores instead of compiling serially. After the build finishes, sudo make install writes the binary, modules, and default support files into the paths from the configure step.
make -j"$(nproc)"
sudo make install
Verify the installed binary before you start replacing the default upstream configuration.
/usr/sbin/nginx -v
nginx version: nginx/1.28.2
If a regular Debian SSH shell ever reports nginx: command not found, the binary is still installed. The common issue is that /usr/sbin is outside the unprivileged PATH, which is why the version checks here use /usr/sbin/nginx.
Write a Debian-Friendly NGINX Configuration
The source build installs the upstream sample nginx.conf, which does not create Debian’s sites-available/sites-enabled layout or a conf.d include by default. The next two blocks replace that sample with a tested Debian-friendly layout, keep temporary files under /var/cache/nginx instead of the web root, and create a simple default server block under /etc/nginx/conf.d/.
The quoted EOF markers matter here. They keep NGINX variables such as $uri literal while sudo tee writes the root-owned files.
sudo install -d -m 0755 /etc/nginx/conf.d /var/cache/nginx /var/log/nginx /var/www/html
sudo tee /etc/nginx/nginx.conf > /dev/null <<'EOF'
user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
server_tokens off;
client_body_temp_path /var/cache/nginx/client_temp;
proxy_temp_path /var/cache/nginx/proxy_temp;
fastcgi_temp_path /var/cache/nginx/fastcgi_temp;
uwsgi_temp_path /var/cache/nginx/uwsgi_temp;
scgi_temp_path /var/cache/nginx/scgi_temp;
include /etc/nginx/conf.d/*.conf;
}
EOF
sudo tee /etc/nginx/conf.d/default.conf > /dev/null <<'EOF'
server {
listen 80;
listen [::]:80;
server_name _;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
EOF
printf '%s\n' '<!doctype html><html><body><h1>NGINX source build on Debian</h1></body></html>' | sudo tee /var/www/html/index.html > /dev/null
Keep only the server {} block in /etc/nginx/conf.d/default.conf. The main http {} block already lives in /etc/nginx/nginx.conf, and copying a second http block into conf.d triggers the familiar http directive is not allowed here error.
Test the new configuration before you hand it to systemd.
sudo /usr/sbin/nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Create the NGINX Systemd Service on Debian
Source-built NGINX does not ship a Debian systemd unit, so create one explicitly and reload systemd before you try to enable the service.
printf '%s\n' \
'[Unit]' \
'Description=The NGINX HTTP and reverse proxy server' \
'After=syslog.target network-online.target remote-fs.target nss-lookup.target' \
'Wants=network-online.target' \
'' \
'[Service]' \
'Type=forking' \
'PIDFile=/var/run/nginx.pid' \
'ExecStartPre=/usr/sbin/nginx -t' \
'ExecStart=/usr/sbin/nginx' \
'ExecReload=/usr/sbin/nginx -s reload' \
'ExecStop=/bin/kill -s QUIT $MAINPID' \
'PrivateTmp=true' \
'' \
'[Install]' \
'WantedBy=multi-user.target' | sudo tee /etc/systemd/system/nginx.service > /dev/null
sudo systemctl daemon-reload
If this Debian host already has the packaged
nginxservice from APT, remove that package before you enable the source-built unit. The packaged service and the source-built service use the samenginx.servicename.
Start and Verify the NGINX Source Build on Debian
Enable the service for future boots, start it now, and then verify both systemd status and a real HTTP response from the local listener.
sudo systemctl enable nginx
sudo systemctl start nginx
systemctl status nginx --no-pager
Relevant output includes:
● nginx.service - The NGINX HTTP and reverse proxy server
Loaded: loaded (/etc/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running)
Process: 9959 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
Process: 9960 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
Main PID: 9962 (nginx)
The HTTP check below is more useful than a browser-only instruction because it proves the listener is answering locally before you involve DNS, firewalls, or another machine.
curl -I http://127.0.0.1
Relevant output includes:
HTTP/1.1 200 OK Server: nginx Content-Type: text/html Content-Length: 79
From another machine, replace 127.0.0.1 with the server IP or DNS name. If this host will be reachable from the network, configure the firewall before you expose ports 80 and 443 with install UFW on Debian.
Troubleshoot NGINX Source Builds on Debian
The three issues below were the most useful ones to keep explicit in this source-build workflow: a missing systemd unit, a port conflict on 80, and dependency mismatches during ./configure.
Fix Unit nginx.service Could Not Be Found on Debian
This error means systemd has not loaded the source-built service file yet, or the file was removed. Recreate the unit from the install section if needed, then reload systemd and start the service again.
sudo systemctl daemon-reload
sudo systemctl enable nginx
sudo systemctl start nginx
Fix Port 80 Already in Use for NGINX on Debian
If NGINX refuses to start because port 80 is already bound, use ss instead of lsof. ss is present on standard Debian installs, so you do not need another package just to inspect the listener.
sudo ss -tlnp | grep ':80'
Relevant output includes a listening process name, for example:
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=1234,fd=4))
Stop the conflicting service before you start the source-built NGINX service again.
sudo systemctl stop apache2
sudo systemctl start nginx
Fix Missing PCRE or OpenSSL Libraries When Building NGINX on Debian
If ./configure stops on a missing PCRE or OpenSSL check, the usual cause is the wrong Debian release package name or a partial dependency install. Debian 13 needs libpcre2-dev, while Debian 12 and Debian 11 still need libpcre3-dev.
sudo apt install build-essential libssl-dev zlib1g-dev libgd-dev curl -y
Then reinstall the PCRE package that matches your Debian release and rerun ./configure from the extracted NGINX source directory.
Update NGINX Source Builds on Debian
Source builds do not receive updates through APT, so it is worth keeping a small helper in /usr/local/bin. The script below checks the stable section of the nginx.org download page, skips work when the installed binary is already current, and preserves your existing /etc/nginx/nginx.conf because make install does not overwrite that file once it exists.
Run the update helper manually instead of from cron. A source upgrade can fail on dependency changes, compiler errors, or configuration issues, and you want that output in front of you before the running service is replaced.
Create the Update NGINX Source Helper on Debian
Install the helper in /usr/local/bin so you can rerun it from any directory with sudo update-nginx-source.
sudo install -m 0755 /dev/stdin /usr/local/bin/update-nginx-source <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
# Rebuild the current nginx.org stable release with the same compile flags.
BUILD_DIR="/usr/local/src/nginx-build"
DOWNLOAD_PAGE="https://nginx.org/en/download.html"
BASE_URL="https://nginx.org/download"
CURRENT_VERSION="$([ -x /usr/sbin/nginx ] && /usr/sbin/nginx -v 2>&1 | sed 's#.*nginx/##' || true)"
TARBALL="$(curl -fsSL "$DOWNLOAD_PAGE" | tr '<' '\n' | sed -n '/h4>Stable version/,/\/table>/p' | grep -Eo 'nginx-[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' | head -n1)"
TARGET_VERSION="$(printf '%s\n' "$TARBALL" | sed 's/^nginx-//; s/\.tar\.gz$//')"
if [ -z "$TARBALL" ] || [ -z "$TARGET_VERSION" ]; then
echo "Could not determine the current stable NGINX tarball." >&2
exit 1
fi
if [ -n "$CURRENT_VERSION" ] && [ "$CURRENT_VERSION" = "$TARGET_VERSION" ]; then
echo "NGINX is already at the current stable release: $CURRENT_VERSION"
exit 0
fi
echo "Preparing build directory: $BUILD_DIR"
install -d -m 0755 "$BUILD_DIR"
cd "$BUILD_DIR"
rm -rf "nginx-$TARGET_VERSION" "$TARBALL"
echo "Downloading $TARBALL"
curl -fLO "$BASE_URL/$TARBALL"
tar -xzf "$TARBALL"
cd "nginx-$TARGET_VERSION"
echo "Configuring NGINX $TARGET_VERSION"
./configure \
--prefix=/var/www/html \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--with-pcre \
--lock-path=/var/lock/nginx.lock \
--pid-path=/var/run/nginx.pid \
--with-http_ssl_module \
--with-http_image_filter_module=dynamic \
--modules-path=/etc/nginx/modules \
--with-http_v2_module \
--with-http_v3_module \
--with-stream=dynamic \
--with-http_addition_module \
--with-http_mp4_module
echo "Compiling NGINX $TARGET_VERSION"
make -j"$(nproc)"
echo "Validating the current configuration"
/usr/sbin/nginx -t
echo "Stopping nginx"
systemctl stop nginx
echo "Installing NGINX $TARGET_VERSION"
make install
echo "Starting nginx"
systemctl start nginx
echo "Update complete"
/usr/sbin/nginx -v
EOF
command -v update-nginx-source
/usr/local/bin/update-nginx-source
Run the Update NGINX Source Helper on Debian
Run the helper as root so it can stop the service, install the new binary, and start NGINX again.
sudo update-nginx-source
NGINX is already at the current stable release: 1.28.2
When nginx.org publishes a newer stable release, the helper downloads it, recompiles it with the same flags, and ends with a fresh nginx version: nginx/x.y.z line.
Remove NGINX Source Builds on Debian
Removing the source build is mostly file cleanup because APT never owned these files. Stop the service first, remove the unit, then remove the binary and the runtime directories that came from the source build.
Stop and Disable NGINX on Debian
Stop the running service before you delete the unit file and the installed binary.
sudo systemctl stop nginx
sudo systemctl disable nginx
Remove the NGINX Source Build Files on Debian
Remove the systemd unit, the source-built binary, the runtime directories, and the reusable build directory.
sudo rm -f /etc/systemd/system/nginx.service
sudo systemctl daemon-reload
sudo rm -f /usr/sbin/nginx
sudo rm -rf /etc/nginx /var/log/nginx /var/cache/nginx /usr/local/src/nginx-build
Review
/var/www/htmlseparately before you delete anything there. That directory often holds real site content, so it is safer to inspect it manually than to remove it by default in a copy-and-paste cleanup block.
Verify NGINX Source Build Removal on Debian
Use direct file checks instead of which nginx. On Debian, /usr/sbin is often outside an unprivileged PATH, so which can return nothing even while the binary still exists.
test ! -x /usr/sbin/nginx && printf 'nginx binary removed\n'
test ! -e /etc/systemd/system/nginx.service && printf 'nginx systemd unit removed\n'
nginx binary removed nginx systemd unit removed
NGINX Source Build on Debian FAQ
Build from source when you need the upstream stable release cadence, specific compile-time modules, or HTTP/3 support outside Debian’s packaged defaults. If you only need a maintained web server with normal Debian integration, the default nginx package or the nginx.org repository is simpler to manage.
The upstream source tarball ships a single sample nginx.conf, not Debian’s packaged virtual-host layout. Replacing that sample with an /etc/nginx/conf.d/ include gives you separate server blocks without pretending the source build behaves like Debian’s package.
The source-built binary in this layout lives at /usr/sbin/nginx, and Debian often leaves /usr/sbin out of an unprivileged SSH user’s PATH. Use /usr/sbin/nginx -v or sudo /usr/sbin/nginx -v when a plain nginx command is not found.
Run sudo /usr/sbin/nginx -V and inspect the configure arguments in the output. That command shows the compiled-in options, and any dynamic modules in this layout are installed under /etc/nginx/modules/.
Conclusion
NGINX built from source is now running on Debian with a systemd unit, a clean conf.d layout, and a stable-track update helper you can rerun when nginx.org publishes the next release. The next practical hardening step is secure NGINX with Let’s Encrypt on Debian, then tighten browser-facing policy with configure security headers in NGINX.
How do I make sure these are all “found” during ./configure ?
checking for sys/filio.h … not found
checking for nobody group … not found
checking for /dev/poll … not found
checking for kqueue … not found
checking for crypt() … not found
checking for F_READAHEAD … not found
checking for F_NOCACHE … not found
checking for directio() … not found
checking for SO_SETFIB … not found
checking for SO_ACCEPTFILTER … not found
checking for SO_BINDANY … not found
checking for IP_BINDANY … not found
checking for IP_RECVDSTADDR … not found
checking for IP_SENDSRCADDR … not found
checking for IP_DONTFRAG … not found
checking for setproctitle() … not found
checking for struct dirent.d_namlen … not found
checking for OpenSSL QUIC support … not found
Thanks for the question, Ben. Good news: those “not found” messages are completely normal and expected on Linux. They do not indicate missing dependencies or a problem with your build.
The NGINX configure script checks for features across all platforms it supports, including BSD, macOS, Solaris, and Linux. The items you listed are platform-specific features that simply do not exist on Linux:
kqueue,SO_ACCEPTFILTER,F_READAHEAD,F_NOCACHE: BSD/macOS kernel features/dev/poll,directio(): Solaris-specific I/O mechanismsSO_SETFIB,SO_BINDANY,IP_BINDANY: FreeBSD/OpenBSD routing featuressetproctitle(),struct dirent.d_namlen: BSD library functionssys/filio.h: BSD header not present on Linuxcrypt(): Checked for HTTP Basic authentication (not needed unless usingauth_basic)Linux uses
epollinstead of kqueue for high-performance I/O, and configure will detect that automatically. You cannot “fix” these checks because the underlying features do not exist on your operating system.The only potentially actionable item is OpenSSL QUIC support. This requires OpenSSL 3.5.1 or higher for native QUIC, which only Debian 13 (Trixie) currently provides. On Debian 11 and 12, NGINX uses its built-in compatibility layer for basic HTTP/3 functionality. For full 0-RTT support on older Debian versions, you would need to compile with BoringSSL or QuicTLS instead of system OpenSSL.
As long as configure completes with a “Configuration summary” showing your paths and libraries, the build will succeed.