Skip to content

Repository files navigation

ngx_lua_config_module

Name

ngx_lua_config_module provides the ngx_http_lua_config_module and ngx_stream_lua_config_module modules. They allow defining key-value configuration items in Nginx configuration, which can then be retrieved through the resty.config Lua API or as nginx variables. The legacy ngx.lua_config C/Lua API remains available for compatibility.

Table of Content

Status

These Nginx modules are currently considered experimental. Issues and PRs are welcome if you encounter any problems.

Synopsis

HTTP

http {
    lua_config server_id my_server_id;

    server {
        listen 80;
        server_name example.com;

        # Local configuration overrides parent configuration
        lua_config server_id my_server_id_A;

        lua_upstream backend {
            server 127.0.0.1:8080 weight=5;
            server 10.0.0.2:8080 level=1;
            keepalive_timeout 60s;
        }

        location /api {
            # Local configuration
            lua_config api_version v1.0;

            # Access via Nginx variables
            add_header X-Env $lua_config_environment;
            add_header X-Server-Region $lua_config_server_region;
            add_header X-Server-Id $lua_config_server_id; # "my_server_id_A"
            add_header X-Api-Version $lua_config_api_version;

            content_by_lua_block {
                -- Access via Lua API
                local lua_config = require "resty.config"
                local env = lua_config.get("environment")
                local region = lua_config.get("server_region")
                local server_id = lua_config.get("server_id") -- "my_server_id_A"
                local api_version = lua_config.get("api_version")

                ngx.log(ngx.INFO, "Env: ", env, ", Region: ", region, ", Server ID: ", server_id, ", API Version: ", api_version)

                -- Access upstream config
                local up = lua_config.get_upstream("backend")
                if up then
                    ngx.log(ngx.INFO, "Backend CRC32: ", up.crc32)
                end

                ngx.say("OK")
            }
        }

        location /another {
            # In another location, server_id will be my_server_id_A
            # api_version is not defined here, so it will be nil
            content_by_lua_block {
                local lua_config = require "resty.config"
                local server_id = lua_config.get("server_id")
                local api_version = lua_config.get("api_version")
                ngx.log(ngx.INFO, "Server ID: ", server_id, ", API Version: ", api_version or "nil")
                ngx.say("OK")
            }
        }
    }
}

Stream

stream {
    lua_init_config app_name stream_gateway;
    lua_config environment production;
    lua_config server_id stream_default;

    lua_upstream backend {
        server 127.0.0.1:8080 weight=5;
        server 10.0.0.2:8080 level=1;
        keepalive_timeout 60s;
    }

    server {
        listen 10000;

        # Server configuration overrides stream-level configuration
        lua_config server_id stream_edge_A;
        lua_config peer $remote_addr;

        content_by_lua_block {
            local lua_config = require "resty.config"

            ngx.say("environment=", lua_config.get("environment"))
            ngx.say("server_id=", lua_config.get("server_id"))

            -- The same item is also available as an nginx variable
            ngx.say("peer=", ngx.var.lua_config_peer)

            local up = lua_config.get_upstream("backend")
            if up then
                ngx.say("backend_crc32=", up.crc32)
            end
        }
    }
}

Installation

To use these modules, configure your Nginx branch with --add-module=/path/to/ngx_lua_config_module.

The same addon registers ngx_http_lua_config_module when HTTP is enabled and ngx_stream_lua_config_module when Stream is enabled. Enable the nginx Stream subsystem (for example, with --with-stream) and build with ngx_stream_lua_module to use the Stream Lua API.

Install lualib/resty/config.lua into the OpenResty Lua library path (normally lualib/resty/config.lua under the OpenResty prefix) to use the FFI API.

To enable named conditions, build ngx_expr_module and this module statically in the same nginx configuration.

Stream dependency

The Stream module currently requires a patched ngx_stream_lua_module. Download ngx_stream_lua_module-expose_request_struct_0.0.18RC2+.patch from the OpenResty patches directory and apply it before building OpenResty with ngx_stream_lua_config_module.

This dependency is still required when using the recommended require "resty.config" FFI API. resty.config obtains an ngx_stream_lua_request_t * from resty.core.base.get_request() and passes it to the Stream FFI functions. The module then reads r->session, so the definition of ngx_stream_lua_request_t must be visible to this external module.

The patch provides two interfaces:

  • It exposes the ngx_stream_lua_request_t definition required by the Stream FFI implementation.
  • It exports ngx_stream_lua_get_request(lua_State *L), which is still used by the legacy require "ngx.lua_config" compatibility API.

Removing the legacy API would eliminate the second requirement, but it would not eliminate the FFI request-structure requirement. Do not remove this patch solely because callers have migrated to resty.config. The HTTP module does not require this Stream-specific patch.

Conditional syntax

Conditional syntax is selected at compile time and has the same behavior in both HTTP and Stream:

  • With ngx_expr_module, use named expr expressions and place lua_config inside when blocks. HTTP supports http, server, and location when contexts; Stream supports stream and server when contexts. Legacy if= and if!= parameters are rejected.
  • Without ngx_expr_module, when is unavailable and legacy if=/if!= parameters remain supported by lua_config.

When a conditional lua_config does not match, the next definition of the same key is evaluated. If no definition matches, that key lookup returns nil.

lua_upstream does not provide conditional-entry syntax. Except for server, each statement in its block is parsed as a regular key-value item.

Directives

lua_config

Syntax: lua_config key string ... [separator=,];

Default: -

HTTP context: http, server, location, when

Stream context: stream, server, when

Defines a key-value configuration item. The key parameter may only contain lowercase letters, numbers, and underscores. The string parameters can contain variables. Multiple string parameters are concatenated using a separator. The default separator is ,. Legacy if= matches a non-empty value other than "0"; if!= matches an empty value or "0". These parameters are available only without ngx_expr_module.

HTTP example:

lua_config data_source primary;
expr has_test_arg !is_empty $arg_test;
when has_test_arg {
    lua_config set_header $arg_test;
}
lua_config allow_methods GET HEAD POST;
lua_config cache_timeout 300s;

Stream example:

stream {
    expr has_ssl_protocol !is_empty $ssl_preread_protocol;
    when has_ssl_protocol {
        lua_config protocol $ssl_preread_protocol;
    }
    lua_config protocol unknown;
}

lua_config_hash_max_size

Syntax: lua_config_hash_max_size number;

Default: lua_config_hash_max_size 512;

HTTP context: http, server, location

Stream context: stream, server

Sets the maximum size of the hash table for storing lua_config key-value pairs.

lua_config_hash_bucket_size

Syntax: lua_config_hash_bucket_size number;

Default: lua_config_hash_bucket_size 32|64|128;

HTTP context: http, server, location

Stream context: stream, server

Sets the bucket size of the hash table for lua_config items. The default value depends on the processor's cache line size. Details on setting up hash tables are provided in a separate document.

lua_upstream

Syntax: lua_upstream name { ... }

Default: -

HTTP context: http, server

Stream context: stream, server

Defines a named upstream configuration block that can be retrieved at request time via the Lua API. The name may only contain lowercase letters, digits, and underscores. Duplicate names within the same context are not allowed. A server-level definition completely overrides a definition with the same name at the enclosing http or stream level; upstream definitions are not merged.

Inside the block, two types of entries are supported:

Server entries:

server host[:port] [level=N] [weight=N] [down];
  • host: An IP address (IPv4 or IPv6 in [addr] notation), domain name, or Unix domain socket path prefixed with unix:. Variables are not allowed.
  • port: Optional port number. Defaults to 0 if omitted.
  • level: Server level, defaults to 1.
  • weight: Server weight, defaults to 1.
  • down: Marks the server as unavailable.

Config items:

key value;                # key-value pair
  • key: Only lowercase letters, digits, and underscores allowed.
  • value: An arbitrary string, supports variables.
  • All tokens after key are value fragments, except for a trailing separator= parameter.

HTTP example:

http {
    lua_upstream backend {
        server 127.0.0.1:8080 level=0 weight=5;
        server [::1]:8081 level=1;
        server backup.example.com:9090 level=2 down;

        keepalive_timeout 60s;
        connect_timeout 5s;
    }

    server {
        # This completely overrides the http-level "backend"
        lua_upstream backend {
            server 10.0.0.1:9090;
        }
    }
}

Stream example:

stream {
    lua_upstream backend {
        server 127.0.0.1:8080 level=0 weight=5;
        server [::1]:8081 level=1;
        server backup.example.com:9090 level=2 down;

        keepalive_timeout 60s;
        connect_timeout 5s;
    }

    server {
        listen 10000;

        # This completely overrides the stream-level "backend"
        lua_upstream backend {
            server 10.0.0.1:9090;
        }
    }
}

lua_init_config

Syntax: lua_init_config key string ... [separator=,];

Default: -

HTTP context: http

Stream context: stream

Defines a static key-value configuration item that is available during the init and init_worker phases, before any request is processed. Unlike lua_config, this directive does not support variables or conditional evaluation: values are plain strings. Multiple string parameters are concatenated using a separator. The default separator is ,. HTTP and Stream maintain separate init-configuration tables.

Example:

http {
    lua_init_config app_name my_application;
    lua_init_config version 1.0.0;
    lua_init_config allowed_origins http://a.com http://b.com http://c.com;
    lua_init_config allowed_methods GET HEAD POST separator=|;
}

stream {
    lua_init_config app_name my_stream_application;
    lua_init_config protocols tcp udp separator=|;
}

Variables

$lua_config_name

Accesses the value of a specific lua_config item by its name. The prefix variable is registered by both modules and resolves against the current HTTP request or Stream session.

HTTP example:

lua_config data_source my_data_source;
add_header My-Config-Value $lua_config_data_source;

Stream example:

stream {
    lua_config data_source my_stream_data_source;

    server {
        listen 10000;

        content_by_lua_block {
            ngx.say(ngx.var.lua_config_data_source)
        }
    }
}

Lua API

The recommended API is loaded with local lua_config = require "resty.config". It uses LuaJIT FFI, detects the current subsystem through ngx.config.subsystem, and selects the HTTP or Stream FFI implementation automatically. Both implementations provide the same result shapes as the legacy require "ngx.lua_config" API.

resty.config.get(key)

Syntax: value = lua_config.get(key)

HTTP context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*

Stream context: preread_by_lua*, content_by_lua*, log_by_lua*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*

Retrieves the value of a specific lua_config item by its key.

  • key: A string representing the key name of the configuration item to query.
  • The value of the corresponding configuration item (string type) if found.
  • nil if the configuration item is not found.

Example:

local lua_config = require "resty.config"
local my_data_source = lua_config.get("data_source")
if my_data_source then
    ngx.log(ngx.INFO, "Data source: ", my_data_source)
else
    ngx.log(ngx.WARN, "Data source not found!")
end

resty.config.get_upstream(name)

Syntax: result = lua_config.get_upstream(name)

HTTP context: server_rewrite_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, precontent_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*, proxy_ssl_certificate_by_lua_*, proxy_ssl_verify_by_lua_*

Stream context: preread_by_lua*, content_by_lua*, log_by_lua*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*

Retrieves the upstream configuration defined by lua_upstream for the given name.

  • name: A string representing the upstream name to look up.
  • Returns nil if the upstream is not found.
  • Returns a table with the following fields:
    • name (string): The upstream name.
    • servers (array): Each entry is a table with:
      • host (string): The server host.
      • port (number): The server port (0 if not specified).
      • level (number): The server level (1 if not specified).
      • weight (number): The server weight.(1 if not specified).
      • down (boolean): Whether the server is marked down.
    • config keys: Each key defined in the block appears as a field. All keys have their resolved string value, with variables evaluated.
    • crc32 (string): A CRC32 checksum (decimal string) computed from the upstream name, all server entries, and all config key-value pairs (keys sorted alphabetically). The checksum changes when any resolved value changes, making it useful for detecting configuration drift.

Example:

local lua_config = require "resty.config"
local up = lua_config.get_upstream("backend")
if not up then
    ngx.say("upstream not found")
    return
end

ngx.say("name: ", up.name)
ngx.say("crc32: ", up.crc32)

for i, srv in ipairs(up.servers) do
    ngx.say("server ", i, ": ", srv.host, ":", srv.port,
            " level=", srv.level, " weight=", srv.weight,
            " down=", tostring(srv.down))
end

if up.keepalive_timeout then
    ngx.say("keepalive_timeout: ", up.keepalive_timeout) -- "60s" (string value)
end

resty.config.get_init_configs()

Syntax: configs = lua_config.get_init_configs()

HTTP context: any

Stream context: any

Returns a table containing all key-value pairs defined by lua_init_config directives for the current subsystem. Returns an empty table if no lua_init_config directives are defined.

Example:

local lua_config = require "resty.config"
local configs = lua_config.get_init_configs()

for k, v in pairs(configs) do
    ngx.log(ngx.INFO, k, " = ", v)
end

Benchmark

bench/benchmark.t compares the legacy API with resty.config in the same request. It alternates execution order across seven rounds, reports the median nanoseconds per operation, and checks that both paths produce the same checksum.

Run it with Test::Nginx and an optimized OpenResty build:

TEST_NGINX_BINARY=/path/to/nginx prove -v bench/benchmark.t

Author

Hanada im@hanada.info

License

This Nginx module is licensed under BSD 2-Clause License.

About

Allows defining key-value configuration items in Nginx configuration for lua modules.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Contributors

Languages