Building NGINX from source gives you complete control over which modules are compiled into your web server, allowing customizations that pre-built packages cannot offer. Whether you need HTTP/3 support for faster connections, custom third-party modules for specialized caching, or specific optimization flags for your hardware, source compilation provides the flexibility to build exactly what your infrastructure requires.
This guide walks through downloading, compiling, and installing NGINX from source on Debian, including creating a systemd service for process management. By the end, you will have a production-ready NGINX installation with HTTP/2, HTTP/3, SSL, and dynamic module support, verified and running as a system service.
Version compatibility: These instructions work on Debian 11 (Bullseye), Debian 12 (Bookworm), and Debian 13 (Trixie). Dependency package names differ on Debian 13 due to the PCRE library transition, so version-specific commands are provided where needed.
Step 1: Update Debian System Packages
First, refresh your package index and upgrade existing packages to ensure you have the latest security patches and library versions:
sudo apt update
sudo apt upgrade
Running these commands before compilation prevents dependency conflicts and also ensures your build tools reference current library versions.
Step 2: Install Build Dependencies
NGINX compilation requires several development libraries. The PCRE library package name differs between Debian versions, so use the command matching your release.
Debian 11 (Bullseye) and Debian 12 (Bookworm):
sudo apt install build-essential libpcre3-dev libssl-dev zlib1g-dev libgd-dev wget -y
Debian 13 (Trixie) and newer:
sudo apt install build-essential libpcre2-dev libssl-dev zlib1g-dev libgd-dev wget -y
Together, these packages provide the compiler toolchain (build-essential), regular expression support (libpcre3-dev or libpcre2-dev), SSL/TLS encryption (libssl-dev), compression (zlib1g-dev), and image processing (libgd-dev) capabilities that NGINX modules require.
HTTP/3 support: NGINX 1.25.0+ includes a built-in OpenSSL compatibility layer that enables HTTP/3 compilation on all Debian versions, including Debian 11’s OpenSSL 1.1.x. This layer converts QUIC handshakes internally, allowing basic HTTP/3 functionality without requiring a patched OpenSSL. However, for full 0-RTT (zero round-trip) early data support, OpenSSL 3.5.1 or higher is required, which only Debian 13 (Trixie) currently provides. For maximum performance, you can alternatively compile with BoringSSL or QuicTLS.
Step 3: Download NGINX Source Code
With dependencies installed, you can now download the NGINX source tarball. Visit the official NGINX download page to check current versions, then download your preferred release. The stable branch receives security fixes and critical bug patches, while mainline includes the latest features. Alternatively, if you prefer a simpler installation without custom modules, see the guide on installing NGINX from Debian packages or the guide on installing NGINX mainline from official repositories.
Download the current stable version:
cd /tmp
wget https://nginx.org/download/nginx-1.28.0.tar.gz
The version number 1.28.0 is the current stable release at the time of writing. Always check the official download page for the latest version and substitute accordingly in all commands below.
Step 4: Extract the Source Archive
Next, extract the downloaded tarball and navigate into the source directory:
tar -xzf nginx-*.tar.gz
cd nginx-*/
By using wildcards (nginx-*), these commands work regardless of the specific version number you downloaded.
Step 5: Configure Build Options
The configure script detects your system and prepares the build with your specified options. Run the following command to configure NGINX with commonly needed 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
Here is what each option configures:
--prefix=/var/www/html: Sets the root directory for web content.--sbin-path=/usr/sbin/nginx: Places the nginx binary in the standard system binary directory.--conf-path=/etc/nginx/nginx.conf: Specifies the main configuration file location.--http-log-pathand--error-log-path: Define log file locations in/var/log/nginx/.--with-pcre: Enables Perl Compatible Regular Expressions for location matching and rewrites.--with-http_ssl_module: Enables HTTPS support for secure connections.--with-http_v2_module: Enables HTTP/2 protocol for improved performance.--with-http_v3_module: Enables HTTP/3 (QUIC) for faster, more reliable connections.--with-http_image_filter_module=dynamic: Adds on-the-fly image resizing as a loadable module.--with-stream=dynamic: Enables TCP/UDP proxying as a loadable module.--with-http_addition_module: Allows adding text before/after responses.--with-http_mp4_module: Enables pseudo-streaming for MP4 video files.
Once configure completes, you should see a configuration summary similar to the following:
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 prefix: "/etc/nginx" 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"
On Debian 11 and 12, the summary displays “PCRE library” instead of “PCRE2 library” since those versions use the older PCRE1 package.
Note: The HTTP/3 module compiles on all Debian versions using NGINX’s compatibility layer. If you require native QUIC with full 0-RTT support, consider compiling NGINX with BoringSSL or QuicTLS instead of system OpenSSL.
Step 6: Compile and Install NGINX
With configuration complete, compile the source code. The -j$(nproc) flag uses all available CPU cores to speed up compilation:
make -j$(nproc)
Compilation typically takes 2-5 minutes depending on your system’s hardware. Once the build finishes, install the compiled binaries:
sudo make install
To confirm successful installation, verify the nginx version:
/usr/sbin/nginx -v
Expected output confirming successful installation:
nginx version: nginx/1.28.0
Step 7: Create Systemd Service File
Since source-compiled NGINX does not include a systemd service file, you need to create one to enable system integration, automatic startup, and standard service management commands:
sudo nano /etc/systemd/system/nginx.service
Add the following service configuration:
[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
Save the file and exit the editor. Afterward, reload systemd to recognize the new service:
sudo systemctl daemon-reload
Important: If you previously installed NGINX via APT (
apt install nginx), remove it first withsudo apt remove --purge nginx nginx-commonbefore creating this service. Both installations use the same service name and cannot coexist.
Step 8: Start and Enable NGINX Service
Start the NGINX service and enable it to launch automatically at boot:
sudo systemctl start nginx
sudo systemctl enable nginx
Next, verify the service is running:
systemctl status nginx
Expected output showing NGINX is active:
● nginx.service - The NGINX HTTP and reverse proxy server
Loaded: loaded (/etc/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Wed 2025-12-11 10:30:00 UTC; 5s ago
Process: 1234 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
Process: 1235 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
Main PID: 1236 (nginx)
Tasks: 2 (limit: 4915)
Memory: 2.5M
CPU: 15ms
CGroup: /system.slice/nginx.service
├─1236 "nginx: master process /usr/sbin/nginx"
└─1237 "nginx: worker process"
Step 9: Test NGINX in Browser
Finally, verify NGINX serves web pages correctly by opening a browser and navigating to your server’s address:
http://localhost
Replace localhost with your server’s IP address if testing remotely. You should see the default NGINX welcome page confirming the server is operational.
Configuration directory note: Source-compiled NGINX uses the
/etc/nginx/conf.d/directory pattern by default for server blocks. This differs from Debian’s packaged NGINX which usessites-available/andsites-enabled/with symlinks. Place your server block configurations in/etc/nginx/conf.d/example.confand they will be automatically included.
Extend NGINX with Additional Modules
One key advantage of source compilation is the ability to add third-party modules. To include an additional module, first download its source code and then use the --add-module flag during configuration:
./configure --add-module=/path/to/nginx-module-source [other options...]
make -j$(nproc)
sudo make install
Popular third-party modules include ngx_pagespeed for automatic optimization, ngx_brotli for improved compression, and headers-more-nginx-module for advanced header manipulation. After recompiling with new modules, always restart NGINX to load them.
Update NGINX to a New Version
Since source-compiled software requires manual updates, you must repeat the build process when a new NGINX version is released. Follow this process:
cd /tmp
wget https://nginx.org/download/nginx-NEW_VERSION.tar.gz
tar -xzf nginx-NEW_VERSION.tar.gz
cd nginx-NEW_VERSION/
./configure [same options as original install]
make -j$(nproc)
sudo /usr/sbin/nginx -t
sudo systemctl stop nginx
sudo make install
sudo systemctl start nginx
/usr/sbin/nginx -v
Replace NEW_VERSION with the actual version number. The nginx -t command validates your configuration still works with the new binary before stopping the service, minimizing downtime. Use the same configure options from your original installation to maintain consistency; the existing configuration files in /etc/nginx/ are preserved during updates.
Avoid automating updates with cron. Compilation can fail due to missing dependencies, upstream changes, or network issues. Always run updates manually so you can monitor output and address problems before they affect your production server.
Remove NGINX Source Installation
If you need to remove the source-compiled NGINX installation, follow these steps to cleanly uninstall all components.
First, stop and disable the service:
sudo systemctl stop nginx
sudo systemctl disable nginx
Remove the systemd service file:
sudo rm /etc/systemd/system/nginx.service
sudo systemctl daemon-reload
Warning: The following commands permanently delete NGINX binaries, configuration files, logs, and web content. Back up any custom configurations or website files before proceeding.
Remove the NGINX binary and installed files:
sudo rm -rf /usr/sbin/nginx
sudo rm -rf /etc/nginx
sudo rm -rf /var/log/nginx
sudo rm -rf /var/www/html
Optionally, remove the source directory used for compilation:
Caution: The following wildcard command removes all directories in
/tmpstarting with “nginx-“. Verify no other important data matches this pattern before running.
sudo rm -rf /tmp/nginx-*/
Verify removal by checking that the nginx binary no longer exists:
which nginx
If removal was successful, this command produces no output (an empty line). Any path returned indicates NGINX binaries still exist.
Additionally, you can remove the build dependencies that are no longer needed:
sudo apt autoremove
This command removes orphaned packages that were installed as dependencies but are no longer required by any installed software.
Troubleshoot Common Issues
If you encounter problems during or after installation, the following solutions address the most common issues.
Port 80 Already in Use
If NGINX fails to start with a “bind() to 0.0.0.0:80 failed” error, another service is using port 80. Identify the conflicting process:
sudo lsof -i :80
Stop the conflicting service (commonly Apache or another NGINX instance) before starting your source-compiled NGINX:
sudo systemctl stop apache2
Configure Fails with Missing Libraries
If ./configure reports missing PCRE, OpenSSL, or zlib libraries, ensure you installed the correct development packages for your Debian version. On Debian 13, remember to use libpcre2-dev instead of libpcre3-dev.
Permission Denied Errors
If NGINX fails to write logs or access configuration files, verify the log directory exists with correct permissions:
sudo mkdir -p /var/log/nginx
sudo chown -R root:root /var/log/nginx
Conclusion
You now have a custom-compiled NGINX installation with HTTP/2, HTTP/3, SSL, and dynamic module support running as a managed systemd service on Debian. This configuration provides the foundation for high-performance web serving, reverse proxying, and load balancing with modules tailored to your specific requirements. Next, consider securing your NGINX server with Let’s Encrypt SSL certificates for production deployments, or explore configuring UFW firewall rules to protect your server from unauthorized access.
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.