Back to Dashboard

openresty

1.31.1.1-alpine

OpenResty with common modules pre-compiled. Multi-arch, SBOM-attested, daily upstream monitoring.

Trust posture

πŸ“‹ SBOM ATTESTEDπŸ›‘ 4 open alertsπŸ— AMD64 + ARM64 πŸ”— 4/4 deps tracked daily πŸ” REPRODUCE THESE CHECKS LOCALLY
PULLS 14.4K 9485 to 14445 pulls across 30 recorded days 2026-09-18: 9485 pulls2026-09-19: 9975 pulls2026-09-20: 10569 pulls2026-09-21: 10893 pulls2026-09-22: 10939 pulls2026-09-23: 10959 pulls2026-09-24: 10988 pulls2026-09-25: 11686 pulls2026-09-26: 11993 pulls2026-09-27: 12019 pulls2026-09-28: 12201 pulls2026-09-29: 12241 pulls2026-09-30: 12421 pulls2026-10-01: 12657 pulls2026-10-02: 12965 pulls2026-10-03: 13217 pulls2026-10-04: 13239 pulls2026-10-05: 13257 pulls2026-10-06: 13263 pulls2026-10-07: 13524 pulls2026-10-08: 13530 pulls2026-10-09: 14051 pulls2026-10-10: 14061 pulls2026-10-11: 14062 pulls2026-10-12: 14199 pulls2026-10-13: 14209 pulls2026-10-14: 14343 pulls2026-10-15: 14393 pulls2026-10-16: 14401 pulls2026-10-17: 14445 pulls
STARS 0

VERIFIABLE TRUST ARTIFACTS

Provenance

Build commit
43b72a4
Build digest
sha256:cc9dda035c69d4c9c0df7579645d27bef056f489d8ceb5feaf21d3671a81c778
Index digest
sha256:488b42b3ee7fbd5b26018afaab49cc5ffd950a482f434c6d1675f7d120246ca6
Manifest digest (amd64)
sha256:ef8b1775b6a4bd961af5d1b3a940b7c7c778d378d05435fdfd0d538906327c6e
Manifest digest (arm64)
sha256:997413f0bc9e5d0815282bc070e3d32b6349b3df820ceac97c16330f1f4723b0
SBOM attestation
54047885…
Trivy evidence
Code Scanning fetched 2026-10-17 11:24 UTC
Base image
ghcr.io/oorabona/library/alpine:latest
Verify
gh attestation verify oci://ghcr.io/oorabona/openresty:1.31.1.1-alpine --owner oorabona guide β†’

Security evidence

Trivy Β· Code Scanning for image 1.31.1.1-alpine fetched 2026-10-17

4 open Code Scanning alerts Β· fetched 2026-10-17 Β· advisory mode (does not block builds)

Code Scanning: 4 open alerts Β· fetched 2026-10-17Recorded scan: 6 findings Β· scanned 2026-10-08
1Critical
2High
1Medium
0Low
0Info

Top advisories (upstream, advisory)

  • CVE-2026-13221 (CRITICAL) β€” openresty β€” perl: Perl: Incorrect regular expression processing via large regular expressions
  • CVE-2026-4775 (HIGH) β€” openresty β€” libtiff: libtiff: Arbitrary code execution or denial of service via signed integer overflow in TIFF file processing
  • CVE-2026-93990 (HIGH) β€” openresty β€” expat: Expat: XML Injection via Malformed UTF-16 Input
  • CVE-2026-46675 (MEDIUM) β€” openresty β€” [Use-after-free of zlib input in `png_read_end` after incomplete zTXt, iTXt or iCCP decompression]

β†’ See verification steps.

Explore

Build lineagecc9dda035c69
Build Lineage
Build Digest sha256:cc9dda035c69d4c9c0df7579645d27bef056f489d8ceb5feaf21d3671a81c778
Base Image ghcr.io/oorabona/library/alpine:latest
RESTY_IMAGE_TAG latest
RESTY_OPENSSL_VERSION 3.5.6
RESTY_OPENSSL_PATCH_VERSION 3.5.5
RESTY_PCRE_VERSION 10.47
RESTY_PCRE_SHA256 c08ae2388ef333e8403e670ad70c0a11f1eed021fd88308d7e02f596fcd9dc16
LUAROCKS_VERSION 3.13.0
NGX_PROXY_CONNECT_VERSION 0.0.7
RESTY_J 4
VERSION 1.31.1.1-alpine
MAJOR_VERSION 1
UPSTREAM_VERSION 1.31.1.1
REMOTE_CR ghcr.io/oorabona
Package summary 55 packages
Recent changes
Build history 10 builds
Dependency health 2 updates available

Dependency Health

2 updates available
4/4 dependencies monitored
Dependency Current Latest Type
RESTY_OPENSSL_VERSION 3.5.6 3.5.9 patch
RESTY_PCRE_VERSION 10.47 10.49 minor
Up-to-date dependencies
LUAROCKS_VERSION 3.13.0 NGX_PROXY_CONNECT_VERSION 0.0.7

Documentation

README

OpenResty

High-performance web platform combining Nginx with LuaJIT for dynamic, programmable request handling.

Docker Hub GHCR Build

Verify this image

Every build ships a Sigstore-signed SBOM and a full Trivy scan β€” verify them yourself, no login required:

gh attestation verify oci://ghcr.io/oorabona/openresty:latest --owner oorabona

Full walkthrough (SBOM payload, Trivy findings, multi-arch manifest inspection, upstream dependency tracking) β†’ https://oorabona.github.io/docker-containers/verify-images/

Quick Start

# Pull from GitHub Container Registry
docker pull ghcr.io/oorabona/openresty:latest

# Pull from Docker Hub
docker pull oorabona/openresty:latest

# Run with custom configuration
docker run -d \
  --name openresty \
  -p 8080:8080 \
  -p 8443:8443 \
  -v ./conf.d:/usr/local/openresty/nginx/conf/conf.d:ro \
  -v ./lua:/usr/local/openresty/lualib/app:ro \
  ghcr.io/oorabona/openresty:latest

Features

  • Nginx + LuaJIT: Full Nginx functionality with embedded LuaJIT for dynamic scripting
  • Built from Source: Compiled with optimized configuration, not based on official image
  • Optional Proxy Connect: Includes ngx_http_proxy_connect_module for forward proxy support
  • LuaRocks: Lua package manager included for easy module installation
  • Extensive Modules: Pre-compiled with HTTP/2, SSL, streaming, and dynamic modules
  • Production Ready: Includes healthcheck, non-root worker processes, and security hardening

Usage

Docker Compose

services:
  openresty:
    image: ghcr.io/oorabona/openresty:latest
    ports:
      - "8080:8080"
      - "8443:8443"
    volumes:
      - ./conf.d:/usr/local/openresty/nginx/conf/conf.d:ro
      - ./lua:/usr/local/openresty/lualib/app:ro
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

Basic Lua Example

Place your nginx configuration in conf.d/:

# conf.d/default.conf
server {
    listen 8080;
    server_name localhost;

    location / {
        content_by_lua_block {
            ngx.say("Hello from OpenResty + Lua!")
        }
    }

    location /api {
        access_by_lua_file /usr/local/openresty/lualib/app/auth.lua;
        proxy_pass http://backend;
    }
}

Custom Lua Modules

Create Lua modules in the lua/ directory:

-- lua/auth.lua
local jwt = require "resty.jwt"

local token = ngx.var.http_authorization
if not token then
    ngx.status = 401
    ngx.say("Missing Authorization header")
    return ngx.exit(401)
end

-- Validate JWT token
local jwt_obj = jwt:verify("secret-key", token)
if not jwt_obj.verified then
    ngx.status = 403
    ngx.say("Invalid token")
    return ngx.exit(403)
end

Installing Lua Packages

The image runs as the non-root nginx user, which can’t write the system LuaRocks tree under /usr/local. Install packages at build time in a derived Dockerfile β€” reset to USER root first, since the base image’s final USER nginx otherwise carries into your RUN steps:

FROM ghcr.io/oorabona/openresty:latest
USER root
RUN luarocks install lua-resty-jwt   # into the system tree, on OpenResty's lua_package_path
USER nginx

Runtime luarocks install --local also works, but it installs into the nginx user’s home tree, which is not on OpenResty’s default lua_package_path β€” you’d have to extend lua_package_path / lua_package_cpath in your nginx config for require to find it. Build-time install into the system tree is the simpler path.

Build Arguments

Argument Description Default
VERSION OpenResty version latest
RESTY_IMAGE_BASE Base image name alpine
RESTY_IMAGE_TAG Base image tag latest
RESTY_VERSION OpenResty version (same as VERSION) ${VERSION}
RESTY_OPENSSL_VERSION OpenSSL version (3.5 LTS) 3.5.6
RESTY_OPENSSL_PATCH_VERSION OpenResty OpenSSL patch version 3.5.5
RESTY_PCRE_VERSION PCRE2 version 10.47
RESTY_PCRE_SHA256 SHA256 digest for the PCRE2 .tar.gz (must match RESTY_PCRE_VERSION; update both together) (see config.yaml)
RESTY_J Parallel build jobs 4
RESTY_CONFIG_OPTIONS Nginx build options (see Dockerfile)
RESTY_CONFIG_OPTIONS_MORE Additional configure options -j${RESTY_J}
RESTY_LUAJIT_OPTIONS LuaJIT compile options (see Dockerfile)
RESTY_ADD_PACKAGE_BUILDDEPS Additional build dependencies Β 
RESTY_ADD_PACKAGE_RUNDEPS Additional runtime dependencies Β 
RESTY_EVAL_PRE_CONFIGURE Commands before configure Β 
RESTY_EVAL_POST_MAKE Commands after make Β 
LUAROCKS_VERSION LuaRocks version 3.13.0
ENABLE_HTTP_PROXY_CONNECT Enable proxy connect module false
NGX_PROXY_CONNECT_VERSION Proxy connect module version 0.0.7

Environment Variables

Variable Description Default
PATH Includes OpenResty binaries /usr/local/openresty/…

OpenResty uses standard Nginx environment variables:

  • Configure via nginx.conf or environment-specific conf files
  • Use envsubst in config files for dynamic variables
  • See Nginx documentation for configuration options

Volumes

Path Description
/usr/local/openresty/nginx/conf/conf.d Nginx configuration files (mount read-only)
/usr/local/openresty/lualib/app Custom Lua modules (mount read-only)
/var/run/openresty Runtime files (use tmpfs)
/var/cache/nginx Cache directory (use tmpfs)
/var/log/nginx Log files (logs to stdout/stderr by default)

Ports

Port Protocol Description
8080 HTTP Default HTTP port
8443 HTTPS Conventional non-root HTTPS port (opt-in β€” provide a cert + listen 8443 ssl;)

The image runs fully as the non-root nginx user, which cannot bind a privileged port, so it listens on 8080 by default (8443 for TLS). Publish these to any host port you like; if you mount your own config, listen on a port β‰₯ 1024.

Security

Process Security

  • Master and Worker Processes: Both run as the non-root nginx user (uid 101, gid 101). The stock config listens on the unprivileged port 8080 (8443 is the conventional, opt-in HTTPS port), so the image needs no capabilities at all β€” compatible with cap_drop: ALL and no-new-privileges.
  • No Shell: nginx user has /sbin/nologin shell
  • Signal Handling: Uses SIGQUIT for clean shutdown

Runtime Hardening

The included docker-compose.yml demonstrates security best practices β€” a non-root image on unprivileged ports, zero capabilities, and an immutable root filesystem:

services:
  openresty:
    image: ghcr.io/oorabona/openresty:latest
    read_only: true              # Immutable root filesystem
    volumes:
      # nginx (non-root) writes its pid/cache/temp files into image-created
      # dirs; a tmpfs over an existing dir isn't reliably writable by the
      # non-root user with the short-form `tmpfs:` list (it can't set a mode),
      # so use the long-form with an octal `mode: 01777` (some Compose versions
      # accept it only as an unquoted integer). One per writable dir:
      - { type: tmpfs, target: /var/run/openresty, tmpfs: { mode: 01777 } }
      - { type: tmpfs, target: /var/cache/nginx, tmpfs: { mode: 01777 } }
      - { type: tmpfs, target: /tmp, tmpfs: { mode: 01777 } }
      - { type: tmpfs, target: /usr/local/openresty/nginx/client_body_temp, tmpfs: { mode: 01777 } }
      - { type: tmpfs, target: /usr/local/openresty/nginx/proxy_temp, tmpfs: { mode: 01777 } }
      - { type: tmpfs, target: /usr/local/openresty/nginx/fastcgi_temp, tmpfs: { mode: 01777 } }
      - { type: tmpfs, target: /usr/local/openresty/nginx/uwsgi_temp, tmpfs: { mode: 01777 } }
      - { type: tmpfs, target: /usr/local/openresty/nginx/scgi_temp, tmpfs: { mode: 01777 } }
    cap_drop:
      - ALL                      # Drop all capabilities (none are needed)
    security_opt:
      - no-new-privileges:true   # Prevent privilege escalation
    ports:
      - "8080:8080"              # HTTP
      - "8443:8443"              # HTTPS β€” nothing listens here until you add a
                                 #         `listen 8443 ssl;` server + a cert

On a normal (writable) rootfs the tmpfs mounts aren’t needed β€” the image writes only to its own already-chowned dirs β€” so read_only plus the tmpfs block is the extra step for an immutable filesystem.

Because it runs as a non-root user, binds only unprivileged ports, and needs no capabilities, the image is a good fit for Kubernetes’ restricted Pod Security Standard β€” set the corresponding pod securityContext (runAsNonRoot: true, runAsUser: 101, allowPrivilegeEscalation: false, capabilities.drop: ["ALL"], and a seccompProfile); a Compose cap_drop/security_opt is not itself a Kubernetes manifest.

Healthcheck

The built-in healthcheck verifies OpenResty is responding. It uses BusyBox wget (curl is not in the runtime image) against / on the non-privileged port the non-root server listens on:

HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
    CMD wget -q -O /dev/null http://localhost:8080/ || exit 1

The built-in check targets :8080 β€” the port the stock config listens on. If you mount a config that listens only on a different port (e.g. 8443, or another unprivileged port), override the healthcheck to match, or the container will report unhealthy even while serving.

If you’d rather probe a dedicated status endpoint, add one to your nginx configuration and point a custom healthcheck at it:

location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    deny all;
}

Dependencies

This container includes the following pinned dependencies:

Dependency Version Monitoring Status Notes
Alpine Linux latest Active Base image
OpenResty (from version.sh) Active Main application
OpenSSL 3.5.6 Pinned (3.5 LTS) LTS, supported to 2030-04-08; migrated from EOL 1.1.1w (#448)
PCRE2 10.47 Active (pinned) Migrated from EOL PCRE1 8.45 (#453 volet 1); tracked-source monitoring redesign tracked in #453
LuaRocks 3.13.0 Active Lua package manager
ngx_http_proxy_connect_module 0.0.7 Active Optional forward proxy support

Note on pinned dependencies:

  • OpenSSL 3.5.6: Pinned to the OpenSSL 3.5 LTS series (supported to 2030-04-08). Migrated from the EOL 1.1.1w pin after openssl.org removed the 1.1.1w tarball and broke the build (#448). OpenResty’s sess_set_get_cb_yield patch (version 3.5.5) is applied for Lua coroutine session-callback yield. monitor: false is retained for now; the tracked-source monitoring redesign is tracked in #453.
  • PCRE2 10.47: Migrated from EOL PCRE1 8.45 (#453 volet 1). Downloaded from github.com/PCRE2Project/pcre2/releases and integrity-verified via SHA256 (RESTY_PCRE_SHA256) before extraction. RESTY_PCRE_VERSION now refers to a PCRE2 version (PCRE1 is retired). When bumping the version, update both RESTY_PCRE_VERSION and RESTY_PCRE_SHA256 together β€” they are coupled to the same .tar.gz asset. monitor:false is retained; tracked-source monitoring redesign is tracked in #453.

Architecture

Build Process

  1. Bootstrap: Downloads and compiles OpenSSL 3.5.x with OpenResty patches
  2. PCRE2 Compilation: Downloads, SHA256-verifies, and builds PCRE2 10.47 with JIT support
  3. Optional Module: Downloads ngx_http_proxy_connect_module if enabled
  4. OpenResty Build: Configures and compiles OpenResty with all modules
  5. LuaRocks Installation: Installs Lua package manager
  6. Cleanup: Removes build dependencies to minimize image size

Included Nginx Modules

Core HTTP Modules:

  • http_ssl_module - HTTPS support
  • http_v2_module - HTTP/2 protocol
  • http_realip_module - Client IP from proxy headers
  • http_addition_module - Add text before/after responses
  • http_sub_module - Response substitution
  • http_dav_module - WebDAV methods
  • http_flv_module - FLV streaming
  • http_mp4_module - MP4 streaming
  • http_gunzip_module - Decompress responses
  • http_gzip_static_module - Serve pre-compressed files
  • http_auth_request_module - External authentication
  • http_random_index_module - Random directory index
  • http_secure_link_module - Signed URL validation
  • http_slice_module - Range request slicing
  • http_stub_status_module - Basic status page

Dynamic Modules:

  • http_geoip_module - Geolocation based on IP
  • http_image_filter_module - Image transformation
  • http_xslt_module - XSLT transformations

Stream Modules:

  • stream_core_module - TCP/UDP load balancing
  • stream_ssl_module - TLS for stream

Mail Modules:

  • mail_core_module - Mail proxy
  • mail_ssl_module - SMTP/IMAP/POP3 SSL

Directory Structure

/usr/local/openresty/
β”œβ”€β”€ bin/
β”‚   β”œβ”€β”€ openresty           # OpenResty binary
β”‚   β”œβ”€β”€ resty               # Resty CLI
β”‚   └── restydoc            # Documentation viewer
β”œβ”€β”€ luajit/
β”‚   └── bin/
β”‚       β”œβ”€β”€ luajit          # LuaJIT interpreter
β”‚       └── luarocks        # Lua package manager
β”œβ”€β”€ lualib/                 # Lua libraries
β”‚   β”œβ”€β”€ resty/              # OpenResty Lua modules
β”‚   └── app/                # Custom modules (mount here)
β”œβ”€β”€ nginx/
β”‚   β”œβ”€β”€ conf/
β”‚   β”‚   β”œβ”€β”€ nginx.conf      # Main configuration
β”‚   β”‚   └── conf.d/         # Additional configs (mount here)
β”‚   β”œβ”€β”€ html/               # Default web root
β”‚   └── logs/               # Logs (symlinked to stdout/stderr)
β”œβ”€β”€ openssl/                # OpenSSL libraries
└── pcre2/                  # PCRE2 libraries

Common Use Cases

API Gateway

upstream backend {
    server api1:3000;
    server api2:3000;
    keepalive 32;
}

server {
    listen 8080;

    location /api/v1 {
        access_by_lua_block {
            -- Rate limiting
            local limit = require "resty.limit.req"
            local lim = limit.new("limit_store", 100, 50)
            local key = ngx.var.remote_addr
            local delay, err = lim:incoming(key, true)
            if not delay then
                if err == "rejected" then
                    return ngx.exit(429)
                end
            end
        }

        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

Web Application Firewall

-- lua/waf.lua
local rules = {
    sql_injection = [[(\%27)|(\')|(\-\-)|(\%23)|(#)]],
    xss = [[(\%3C)|(<)|(\%3E)|(>)|(\%3c)|(\%3e)]],
}

local uri = ngx.var.uri
local args = ngx.var.args or ""

for rule_name, pattern in pairs(rules) do
    if string.match(uri, pattern) or string.match(args, pattern) then
        ngx.log(ngx.ERR, "WAF: Blocked ", rule_name, " attempt")
        return ngx.exit(403)
    end
end

Caching Proxy

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=cache:10m max_size=1g inactive=60m;

server {
    listen 8080;

    location / {
        proxy_cache cache;
        proxy_cache_valid 200 60m;
        proxy_cache_valid 404 1m;
        proxy_cache_key "$scheme$request_method$host$request_uri";
        proxy_cache_bypass $http_cache_control;
        add_header X-Cache-Status $upstream_cache_status;

        proxy_pass http://origin;
    }
}

Dynamic Load Balancing

-- lua/balancer.lua
local balancer = require "ngx.balancer"
local redis = require "resty.redis"

-- Get healthy backends from Redis
local red = redis:new()
red:connect("redis", 6379)
local backends = red:smembers("healthy_backends")

-- Round-robin selection
local current = ngx.shared.balance:incr("counter", 1, 0)
local index = (current % #backends) + 1
local backend = backends[index]

-- Set upstream server
local ok, err = balancer.set_current_peer(backend)
if not ok then
    ngx.log(ngx.ERR, "failed to set peer: ", err)
    return ngx.exit(500)
end

Performance Tips

  • Connection Pooling: Use keepalive upstream connections
  • Lua Shared Dictionaries: Cache data across workers with lua_shared_dict
  • LuaJIT Optimization: Profile code with -jdump for hotspots
  • Worker Processes: Set to number of CPU cores
  • Sendfile: Enable for static file serving
  • TCP Nopush: Reduce network overhead
  • Gzip: Compress responses (or use gzip_static)

Version Management

# Check current version
./version.sh

# Check latest upstream version
./version.sh latest

# Output JSON for automation
./version.sh --json

Building Locally

# From repository root
./make build openresty

# With specific version
./make build openresty 1.25.3.2

# With proxy connect module
docker build \
  --build-arg ENABLE_HTTP_PROXY_CONNECT=true \
  -t openresty:latest .