Skip to main content

NGINX

Lua NGINX Module Rocky Linux 10: Scripting Guide

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

The Lua NGINX module Rocky Linux 10 brings powerful scripting capabilities to your web server. This module embeds the LuaJIT interpreter directly into NGINX, enabling dynamic request processing, custom authentication, complex routing logic, and API gateway functionality without external dependencies.

This comprehensive guide shows you how to install and configure the Lua NGINX module on Rocky Linux 10, AlmaLinux 10, and other Enterprise Linux 10 distributions. You will learn basic Lua scripting, common use cases, and production patterns. By the end, you will have the Lua NGINX module Rocky Linux 10 ready for custom server-side logic.

Why Use the Lua NGINX Module

The Lua NGINX module transforms NGINX from a static web server into a programmable application platform. Key benefits include:

  • High performance: LuaJIT compiles to native machine code for near-C performance
  • Non-blocking I/O: Cosocket API enables asynchronous operations without blocking workers
  • Flexible request handling: Modify requests and responses at any processing phase
  • Custom authentication: Implement OAuth, JWT validation, or custom auth schemes
  • API gateway features: Rate limiting, request transformation, and routing logic
  • No external processes: Everything runs inside NGINX worker processes

The module powers OpenResty, a popular web platform used by companies like Cloudflare, Kong, and Adobe. Installing it standalone provides the same capabilities with a simpler setup.

Prerequisites

Before installing the Lua NGINX module Rocky Linux 10 packages, ensure you have:

  • Rocky Linux 10, AlmaLinux 10, RHEL 10, or Oracle Linux 10
  • Root or sudo access to the server
  • NGINX installed (or we will install it in this guide)

The GetPageSpeed repository provides pre-built Lua modules with all dependencies included.

Installation

Step 1: Configure GetPageSpeed Repository

The GetPageSpeed repository provides enterprise-grade NGINX modules including the Lua module. Install the repository:

sudo dnf -y install https://extras.getpagespeed.com/release-latest.rpm

Step 2: Install NGINX

If NGINX is not already installed:

sudo dnf -y install nginx
sudo systemctl enable --now nginx

Step 3: Install Lua Module

Install the NGINX Lua module:

sudo dnf -y install nginx-module-lua

This installs several components:

  • nginx-module-lua: The main Lua module
  • nginx-module-ndk: NGINX Development Kit (required dependency)
  • lua5.1-resty-core: Core Lua library for NGINX
  • lua5.1-resty-lrucache: LRU cache library

After installation, you will see:

----------------------------------------------------------------------

The nginx-module-lua has been installed.
To enable this module, add the following to /etc/nginx/nginx.conf
and reload nginx:

    load_module modules/ndk_http_module.so;
    load_module modules/ngx_http_lua_module.so;

----------------------------------------------------------------------

Step 4: Enable the Module

Add the module loading directives at the top of your NGINX configuration. The NDK module must load before the Lua module:

# /etc/nginx/nginx.conf
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_lua_module.so;

user nginx;
worker_processes auto;
# ... rest of configuration

Step 5: Basic Configuration

Add a simple Lua endpoint to test the module:

http {
    server {
        listen 80;

        location /lua {
            default_type text/plain;
            content_by_lua_block {
                ngx.say("Hello from Lua!")
                ngx.say("NGINX version: " .. ngx.var.nginx_version)
            }
        }
    }
}

Test and reload:

sudo nginx -t && sudo systemctl reload nginx

Verify the module works:

curl http://localhost/lua

Output:

Hello from Lua!
NGINX version: 1.28.1

Understanding Processing Phases

The Lua module provides handlers for different NGINX processing phases:

Phase Directive Purpose
init init_by_lua_block Global initialization at startup
init_worker init_worker_by_lua_block Per-worker initialization
set set_by_lua_block Set NGINX variables
rewrite rewrite_by_lua_block URL rewriting and redirection
access access_by_lua_block Authentication and access control
content content_by_lua_block Generate response content
header_filter header_filter_by_lua_block Modify response headers
body_filter body_filter_by_lua_block Modify response body
log log_by_lua_block Custom logging

Each phase runs at a specific point in request processing. Choose the appropriate phase for your logic.

Common Use Cases

Custom Authentication

Implement basic authentication with Lua:

location /protected {
    access_by_lua_block {
        local auth = ngx.var.http_authorization
        if not auth then
            ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted"'
            ngx.exit(401)
        end

        local encoded = string.match(auth, "Basic%s+(.+)")
        if not encoded then
            ngx.exit(403)
        end

        local decoded = ngx.decode_base64(encoded)
        local user, pass = string.match(decoded, "(.+):(.+)")

        if user ~= "admin" or pass ~= "secret" then
            ngx.exit(403)
        end
    }

    proxy_pass http://backend;
}

JWT Token Validation

Validate JWT tokens without external services:

location /api {
    access_by_lua_block {
        local jwt = require "resty.jwt"
        local auth = ngx.var.http_authorization

        if not auth then
            ngx.status = 401
            ngx.say('{"error": "No token provided"}')
            ngx.exit(401)
        end

        local token = string.match(auth, "Bearer%s+(.+)")
        local secret = "your-secret-key"

        local jwt_obj = jwt:verify(secret, token)
        if not jwt_obj.verified then
            ngx.status = 401
            ngx.say('{"error": "Invalid token"}')
            ngx.exit(401)
        end

        ngx.var.user_id = jwt_obj.payload.sub
    }

    proxy_pass http://backend;
}

Note: This requires the lua-resty-jwt library.

Dynamic Rate Limiting

Implement custom rate limiting with shared memory:

http {
    lua_shared_dict rate_limit 10m;

    server {
        location /api {
            access_by_lua_block {
                local limit = ngx.shared.rate_limit
                local key = ngx.var.remote_addr
                local requests = limit:incr(key, 1, 0, 60)

                if requests > 100 then
                    ngx.status = 429
                    ngx.header["Retry-After"] = 60
                    ngx.say("Rate limit exceeded")
                    ngx.exit(429)
                end
            }

            proxy_pass http://backend;
        }
    }
}

Request Logging to External Service

Send request data to an analytics service:

location / {
    log_by_lua_block {
        local http = require "resty.http"
        local httpc = http.new()

        local data = {
            uri = ngx.var.uri,
            status = ngx.var.status,
            time = ngx.var.request_time,
            ip = ngx.var.remote_addr
        }

        httpc:request_uri("http://analytics.internal/log", {
            method = "POST",
            body = require("cjson").encode(data),
            headers = { ["Content-Type"] = "application/json" }
        })
    }

    proxy_pass http://backend;
}

Response Modification

Inject content into HTML responses:

location / {
    body_filter_by_lua_block {
        local chunk = ngx.arg[1]
        if chunk then
            chunk = string.gsub(chunk, "</body>",
                '<script src="/analytics.js"></script></body>')
            ngx.arg[1] = chunk
        end
    }

    proxy_pass http://backend;
}

Dynamic Upstream Selection

Route requests based on custom logic:

http {
    upstream backend_a {
        server 10.0.0.1:8080;
    }

    upstream backend_b {
        server 10.0.0.2:8080;
    }

    server {
        location /api {
            set $backend "";

            rewrite_by_lua_block {
                local user_type = ngx.var.cookie_user_type

                if user_type == "premium" then
                    ngx.var.backend = "backend_a"
                else
                    ngx.var.backend = "backend_b"
                end
            }

            proxy_pass http://$backend;
        }
    }
}

Shared Memory Dictionaries

The Lua module provides shared memory for data sharing across workers:

http {
    # Allocate 10MB for shared data
    lua_shared_dict my_cache 10m;

    server {
        location /cache {
            content_by_lua_block {
                local cache = ngx.shared.my_cache

                -- Set with 60 second expiration
                cache:set("key", "value", 60)

                -- Get value
                local value = cache:get("key")

                -- Increment counter
                local count = cache:incr("visits", 1, 0)

                ngx.say("Value: ", value)
                ngx.say("Visits: ", count)
            }
        }
    }
}

Shared dictionaries support:

  • get(key) – Retrieve value
  • set(key, value, exptime) – Store value
  • incr(key, value, init, init_ttl) – Atomic increment
  • delete(key) – Remove key
  • flush_all() – Clear all data

Asynchronous Operations

The cosocket API enables non-blocking network operations:

location /fetch {
    content_by_lua_block {
        local http = require "resty.http"
        local httpc = http.new()

        -- Non-blocking HTTP request
        local res, err = httpc:request_uri("http://api.example.com/data")

        if not res then
            ngx.status = 502
            ngx.say("Failed: ", err)
            return
        end

        ngx.say(res.body)
    }
}

Install the HTTP client library:

sudo dnf -y install lua5.1-resty-http

Debugging and Logging

Use NGINX error log for debugging:

content_by_lua_block {
    ngx.log(ngx.ERR, "Debug message")
    ngx.log(ngx.WARN, "Variable value: ", ngx.var.uri)
}

View logs:

sudo tail -f /var/log/nginx/error.log

Performance Considerations

Optimize Lua code for production:

  1. Pre-compile code: Use init_by_lua_block for one-time operations
  2. Cache connections: Reuse database and HTTP connections
  3. Use shared dictionaries: Avoid repeated calculations
  4. Minimize string operations: Use table.concat for building strings
  5. Avoid global variables: Use local variables for better performance

Example of optimized initialization:

http {
    init_by_lua_block {
        -- Load modules once at startup
        cjson = require "cjson"
        http = require "resty.http"
    }

    server {
        location /api {
            content_by_lua_block {
                -- Use pre-loaded modules
                local data = cjson.decode(ngx.req.get_body_data())
                ngx.say(cjson.encode({ status = "ok" }))
            }
        }
    }
}

Troubleshooting

Module Not Loading

Symptom: unknown directive "content_by_lua_block"

Solutions:

  1. Verify both modules are loaded (NDK must come first)
  2. Check module files exist: ls /usr/lib64/nginx/modules/
  3. Ensure directives are in the correct context

Lua Errors

Symptom: 500 errors with Lua stack traces

Solutions:

  1. Check error log: tail /var/log/nginx/error.log
  2. Wrap code in pcall for graceful error handling
  3. Verify required libraries are installed

Memory Issues

Symptom: Worker processes consuming excessive memory

Solutions:

  1. Set memory limits: lua_max_running_timers 256
  2. Use shared dictionaries instead of per-request tables
  3. Explicitly clear large variables

SELinux Compatibility

The nginx-module-lua package from GetPageSpeed works correctly with SELinux in enforcing mode. No additional policy configuration is required.

Complete Production Configuration

Here is a production-ready Lua NGINX module Rocky Linux 10 configuration:

# /etc/nginx/nginx.conf
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_lua_module.so;

user nginx;
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;

    # Shared memory for rate limiting and caching
    lua_shared_dict rate_limit 10m;
    lua_shared_dict cache 50m;

    # Pre-load modules at startup
    init_by_lua_block {
        cjson = require "cjson"
    }

    sendfile on;
    keepalive_timeout 65;

    server {
        listen 80;
        server_name example.com;

        location / {
            root /usr/share/nginx/html;
            index index.html;
        }

        location /api {
            default_type application/json;
            content_by_lua_block {
                ngx.say(cjson.encode({
                    status = "ok",
                    server = ngx.var.server_name,
                    time = ngx.now()
                }))
            }
        }
    }
}

Test and apply:

sudo nginx -t && sudo systemctl reload nginx

For more NGINX configuration guides:

Conclusion

The Lua NGINX module Rocky Linux 10 extends NGINX into a programmable application platform. With the GetPageSpeed repository, installation includes all dependencies and works immediately with SELinux enabled.

The module enables custom authentication, dynamic routing, rate limiting, and API gateway features without external services. LuaJIT provides excellent performance, and the non-blocking cosocket API handles concurrent operations efficiently.

Start with simple content handlers and gradually add complexity as needed. The processing phase system gives precise control over when code executes during request handling. For production deployments, pre-load modules at startup and use shared dictionaries for data persistence across requests.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.