Hello, I'm Maneshwar. I'm building git-lrc, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. Star Us to help devs discover the project. Do give it a try and share your feedback for improving the product.
Hi there! I’m building a first-of-its-kind tool that helps you automatically index API endpoints across all your repositories. makes it easier to discover, understand, and interact with APIs in large infrastructures.NGINX is a powerful and versatile web server, and its extensibility through modules is one of its greatest strengths.
Sometimes, however, you need functionality that isn't available out-of-the-box or as a pre-compiled dynamic module.
This is where custom NGINX modules come in.
In this blog post, we'll walk through a real-world scenario: building NGINX with the ngx_http_consul_backend_module and the ngx_devel_kit (NDK) module.
We'll leverage Ansible to automate the entire process, making it repeatable, reliable, and dev-friendly.
Why Ansible for NGINX Builds?
Manually compiling NGINX with custom modules can be a tedious and error-prone process.
Dependencies, compilation flags, and directory structures all need to be precisely managed.
Ansible simplifies this significantly by:
- Idempotency: Tasks can be run multiple times without causing unintended side effects.
- Automation: Automate repetitive tasks, saving time and reducing human error.
- Version Control: Store your build process in version control, enabling easy collaboration and rollbacks.
- Consistency: Ensure consistent builds across different environments.
Our Goal: NGINX with Consul Backend Module
We'll be building NGINX 1.23.2 and integrating two specific modules:
-
ngx_devel_kit(NDK): A collection of utilities and APIs that simplify the development of NGINX modules. Many custom modules rely on NDK. -
ngx_http_consul_backend_module: A module that allows NGINX to discover backend services from HashiCorp Consul. This is particularly useful in dynamic, microservices-based environments.
Project Structure
Our Ansible project is organized as a role named nginx-with-consul-module.
This structure promotes reusability and maintainability.
ansible
├─ README.md
├─ ansible.cfg
├─ hosts.ini
├─ install_ansible.sh
├─ nginx-build-playbook.yml
└─ roles
└─ nginx-with-consul-module
├─ tasks
│ ├─ build_consul_backend_module.yml
│ ├─ build_nginx.yml
│ ├─ configure_build.yml
│ ├─ download_sources.yml
│ ├─ install_dependencies.yml
│ ├─ main.yml
│ ├─ purge_deps.yml
│ └─ systemd.yml
└─ templates
└─ nginx.service.j2
The main.yml in the tasks directory orchestrates the entire build process by importing other task files:
---
- import_tasks: purge_deps.yml
- import_tasks: install_dependencies.yml
- import_tasks: download_sources.yml
- import_tasks: build_consul_backend_module.yml
- import_tasks: configure_build.yml
- import_tasks: build_nginx.yml
- import_tasks: systemd.yml
Let's break down each step.
Step 1: Install Dependencies (install_dependencies.yml)
Building NGINX and its modules requires several development tools and libraries.
This task ensures all necessary packages are present on the target system.
---
- name: Install required packages
apt:
name:
- build-essential # For gcc, g++ and make
- libpcre3
- libpcre3-dev # For PCRE regular expressions support
- zlib1g
- zlib1g-dev # For gzip compression support
- libssl-dev # For OpenSSL (HTTPS) support
- git # To clone the Consul backend module
- wget # To download source archives
- curl # General utility
state: present
update_cache: true
Step 2: Download Sources (download_sources.yml)
Before we can compile anything, we need the source code for NGINX, NDK, and the Consul backend module.
We'll download these to a temporary directory.
---
- name: Create build directory
file:
path: /tmp
state: directory
- name: Download nginx 1.23.2 source
get_url:
url: https://nginx.org/download/nginx-1.23.2.tar.gz
dest: /tmp/nginx.tgz
- name: Extract nginx source
unarchive:
src: /tmp/nginx.tgz
dest: /tmp/
remote_src: yes
- name: Download NDK module
get_url:
url: https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
dest: /tmp/ngx_devel_kit-0.3.0.tgz
- name: Extract NDK module
unarchive:
src: /tmp/ngx_devel_kit-0.3.0.tgz
dest: /tmp/
remote_src: yes
- name: Clone Consul backend module
git:
repo: https://github.com/hashicorp/ngx_http_consul_backend_module.git
dest: /go/src/github.com/hashicorp/ngx_http_consul_backend_module
A crucial step here for the Consul backend module is a small patch.
The original code's strlen call with backend might cause a warning or error with newer compilers due to type mismatch.
We're explicitly casting backend to (const char*) to resolve this.
- name: Replace backend string length calculation
replace:
path: /go/src/github.com/hashicorp/ngx_http_consul_backend_module/src/ngx_http_consul_backend_module.c
regexp: 'ngx_str_t ngx_backend = { strlen\(backend\), backend };'
replace: "ngx_str_t ngx_backend = { strlen((const char*)backend), backend };"
Step 3: Build Consul Backend Module (build_consul_backend_module.yml)
The ngx_http_consul_backend_module is written in Go and needs to be compiled as a C shared library. This involves several steps:
---
- name: Ensure nginx ext directory exists
file:
path: /usr/local/nginx/ext/
state: directory
owner: "{{ ansible_user | default('root') }}"
group: "{{ ansible_user | default('root') }}"
mode: "0755"
- name: Change ownership of Consul backend module directory
file:
path: /go/src/github.com/hashicorp/ngx_http_consul_backend_module
state: directory
owner: "{{ ansible_user | default('root') }}"
group: "{{ ansible_user | default('root') }}"
recurse: yes
- name: Check Go version
shell: export PATH=/usr/local/go/bin:$PATH && /usr/local/go/bin/go version
register: go_version_result
changed_when: false
failed_when: false
- name: Initialize Go modules
command: /usr/local/go/bin/go mod init github.com/hashicorp/ngx_http_consul_backend_module
args:
chdir: /go/src/github.com/hashicorp/ngx_http_consul_backend_module
register: go_mod_init_result
changed_when: go_mod_init_result.rc == 0
failed_when: go_mod_init_result.rc != 0 and "go.mod already exists" not in go_mod_init_result.stderr
ignore_errors: true # Ignore if go.mod already exists
- name: Tidy Go modules
command: /usr/local/go/bin/go mod tidy
args:
chdir: /go/src/github.com/hashicorp/ngx_http_consul_backend_module
- name: Print Go version
debug:
msg: "{{ go_version_result.stdout }}"
- name: Build Go shared library for Consul backend module
shell: |
export PATH=/usr/local/go/bin:$PATH
CGO_CFLAGS="-I /tmp/ngx_devel_kit-0.3.0/src" \
/usr/local/go/bin/go build -buildmode=c-shared -o /usr/local/nginx/ext/ngx_http_consul_backend_module.so ./src/ngx_http_consul_backend_module.go
args:
chdir: /go/src/github.com/hashicorp/ngx_http_consul_backend_module
Key points:
- We create a dedicated directory
/usr/local/nginx/ext/to store our compiled dynamic module. - We ensure correct ownership for the cloned Consul module directory.
- We initialize and tidy Go modules to manage dependencies.
- The
go build -buildmode=c-sharedcommand is crucial. It compiles the Go module into a C shared library (.sofile) that NGINX can load dynamically. -
CGO_CFLAGS="-I /tmp/ngx_devel_kit-0.3.0/src"is important because the Consul module depends on headers from the NDK, so we need to tell the Go compiler where to find them.
Step 4: Configure NGINX Build (configure_build.yml)
This step involves running the ./configure script for NGINX. This script generates the Makefile based on the desired modules and features.
# Simplified for brevity, but this is where you'd run ./configure
# with all your desired flags and --add-module directives.
# The original configure command from the user's prompt (commented out) would be used here.
# Example of how it would look if it were a separate task:
- name: Configure NGINX build
command: >
./configure
--prefix=/etc/nginx
--sbin-path=/usr/sbin/nginx
--conf-path=/etc/nginx/nginx.conf
--pid-path=/var/run/nginx.pid
--lock-path=/var/run/nginx.lock
--error-log-path=/var/log/nginx/error.log
--http-log-path=/var/log/nginx/access.log
--with-http_ssl_module
--with-http_stub_status_module
--with-http_realip_module
--with-http_auth_request_module
--with-http_v2_module
--with-http_dav_module
--with-http_slice_module
--with-http_addition_module
--with-http_gunzip_module
--with-http_gzip_static_module
--with-http_sub_module
--with-mail_ssl_module
--with-stream_ssl_module
--with-debug
--add-module=/tmp/ngx_devel_kit-0.3.0
--add-module=/go/src/github.com/hashicorp/ngx_http_consul_backend_module
args:
chdir: /tmp/nginx-1.23.2
Important configure flags:
-
--prefix=/etc/nginx: Sets the installation prefix for NGINX. -
--add-module=/path/to/module: This is critical for including our custom modules. We point to the extracted NDK and the cloned Consul backend module source directories. - Other
--with-*flags enable various built-in NGINX modules like SSL, HTTP/2, etc.
Step 5: Build and Install NGINX (build_nginx.yml)
Once configure has generated the Makefile, we can proceed with compilation and installation.
---
- name: Compile nginx
command: make
args:
chdir: /tmp/nginx-1.23.2
- name: Install nginx
command: make install
args:
chdir: /tmp/nginx-1.23.2
- name: Install apache2-utils # Useful for htpasswd, etc.
apt:
name: apache2-utils
state: present
-
make: Compiles the NGINX source code along with the added modules. -
make install: Installs NGINX and its components to the paths specified during theconfigurestep.
Step 6: Systemd Integration (systemd.yml)
For a production-ready setup, we need NGINX to run as a system service.
This task typically involves creating a systemd service file.
# Example content for systemd.yml, assuming you have nginx.service.j2
---
- name: Copy nginx systemd service file
template:
src: nginx.service.j2
dest: /etc/systemd/system/nginx.service
owner: root
group: root
mode: '0644'
notify: Reload systemd
- name: Enable nginx service
systemd:
name: nginx
enabled: true
daemon_reload: true # Ensures systemd picks up the new service file
state: started
# Handler for 'Reload systemd'
# handlers/main.yml
# ---
# - name: Reload systemd
# systemd:
# daemon_reload: true
The nginx.service.j2 template would define how NGINX starts, stops, and reloads as a systemd service.
Step 7: Cleanup (Optional but Recommended - purge_deps.yml)
After a successful build, you might want to remove temporary build files and even some build dependencies to free up space, especially in a containerized environment.
# Example content for purge_deps.yml
---
- name: Clean up temporary build files
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/nginx.tgz
- /tmp/nginx-1.23.2
- /tmp/ngx_devel_kit-0.3.0.tgz
- /tmp/ngx_devel_kit-0.3.0
- /go/src/github.com/hashicorp/ngx_http_consul_backend_module
Running the Ansible Playbook
To execute this entire process, you would have a playbook like nginx-build-playbook.yml:
---
- name: Build NGINX with Consul module
hosts: your_nginx_servers
become: yes # Run tasks with sudo/root privileges
roles:
- nginx-with-consul-module
And run it with:
ansible-playbook nginx-build-playbook.yml -i hosts.ini
Conclusion
Building NGINX with custom modules can seem daunting, but by breaking down the process into logical Ansible tasks, we can create a robust, automated, and easily repeatable solution.
This approach is invaluable for consistent deployments across development, testing, and production environments.
You now have a solid foundation for integrating any custom NGINX module into your infrastructure using the power of Ansible!
helps you get all your backend APIs documented in a few minutes.
With , you can generate interactive API docs that allow users to search and execute endpoints directly from the browser.
If you're tired of updating manually or syncing collections, give it a shot.
*AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.
git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.*
Any feedback or contributors are welcome! It's online, source-available, and ready for anyone to use.
⭐ Star it on GitHub:
HexmosTech
/
git-lrc
Free, Unlimited AI Code Reviews That Run on Commit
| 🇩🇰 Dansk | 🇪🇸 Español | 🇮🇷 Farsi | 🇫🇮 Suomi | 🇯🇵 日本語 | 🇳🇴 Norsk | 🇵🇹 Português | 🇷🇺 Русский | 🇦🇱 Shqip | 🇨🇳 中文 |
git-lrc
Free, Unlimited AI Code Reviews That Run on Commit
AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.
git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.
See It In Action
See git-lrc catch serious security issues such as leaked credentials, expensive cloud operations, and sensitive material in log statements
git-lrc-intro-60s.mp4
Why
- 🤖 AI agents silently break things. Code removed. Logic changed. Edge cases gone. You won't notice until production.
- 🔍 Catch it before it ships. AI-powered inline comments show you exactly what changed and what looks wrong.
- 🔁 Build a…
Top comments (4)
Love how you broke the process into clear Ansible roles, really makes custom NGINX builds less of a headache. How do you usually handle upgrades for these modules later on - have you automated that part as well?
Thanks
This is gold for DevOps beginners and pros alike ...
Haha so true