How to Build NGINX from Source on Debian 13, 12 and 11

Last updated Tuesday, March 24, 2026 12:05 pm 12 min read 2 comments

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_module flag 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 nginx service from APT, remove that package before you enable the source-built unit. The packaged service and the source-built service use the same nginx.service name.

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/html separately 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

When should I build NGINX from source on Debian instead of using APT?

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.

Why does a source-built NGINX on Debian not use sites-available and sites-enabled by default?

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.

Why does nginx return command not found in some Debian SSH sessions?

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.

How can I check which modules this NGINX build includes on Debian?

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.

Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffee Buy me a coffee

2 thoughts on “How to Build NGINX from Source on Debian 13, 12 and 11”

  1. 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

    Reply
    • 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 mechanisms
      • SO_SETFIB, SO_BINDANY, IP_BINDANY: FreeBSD/OpenBSD routing features
      • setproctitle(), struct dirent.d_namlen: BSD library functions
      • sys/filio.h: BSD header not present on Linux
      • crypt(): Checked for HTTP Basic authentication (not needed unless using auth_basic)

      Linux uses epoll instead 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.

      Reply
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Leave a Comment

We read and reply to every comment - let us know how we can help or improve this guide.

Let us know you are human: