openresty
1.31.1.1-alpineOpenResty with common modules pre-compiled. Multi-arch, SBOM-attested, daily upstream monitoring.
Trust posture
VERIFIABLE TRUST ARTIFACTS
Provenance
- Build commit
8a8b07b- Build digest
-
sha256:ef26e8ff96ba6bd087f0281619ab7043b02f7a3e8a18f2af96473a7d96ee25a7 - Index digest
-
sha256:04cc9a85fdcdcee0751ee066b7c8c9ba1e508dfe98089f6b2a3f9278e224f220 - Manifest digest (amd64)
-
sha256:d58c6d28683470f324ef05700634ac4a5e9f4dd4c1cef7b046f5a7e5c37aa0e8 - Manifest digest (arm64)
-
sha256:9dd9457aca5d9912cf715479f119dc9f16891abc2237cd1853af7909e15167f2 - SBOM attestation
-
43788953β¦ - Trivy last scan
- 2026-08-29 00:47 UTC
- Base image
- Verify
gh attestation verify oci://ghcr.io/oorabona/openresty:1.31.1.1-alpine --owner oorabonaguide β
Security scan results
Trivy Β· last scan 2026-08-29
β Full report via gh api β see Verify Images.
Explore
Build lineagesha256:
Package summary n/a β runtime parsed
Recent changes n/a β runtime parsed
Build history n/a β runtime fetched
Dependency health 2 updates available
Dependency Health
2 updates available| Dependency | Current | Latest | Type |
|---|---|---|---|
| RESTY_OPENSSL_VERSION | 3.5.6 | 3.5.8 | patch |
| RESTY_PCRE_VERSION | 10.47 | 10.48 | minor |
Up-to-date dependencies
Documentation
README
OpenResty
High-performance web platform combining Nginx with LuaJIT for dynamic, programmable request handling.
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
envsubstin 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
nginxuser (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 withcap_drop: ALLandno-new-privileges. - No Shell: nginx user has
/sbin/nologinshell - 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_yieldpatch (version 3.5.5) is applied for Lua coroutine session-callback yield.monitor: falseis 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/releasesand integrity-verified via SHA256 (RESTY_PCRE_SHA256) before extraction.RESTY_PCRE_VERSIONnow refers to a PCRE2 version (PCRE1 is retired). When bumping the version, update bothRESTY_PCRE_VERSIONandRESTY_PCRE_SHA256together β they are coupled to the same.tar.gzasset.monitor:falseis retained; tracked-source monitoring redesign is tracked in #453.
Architecture
Build Process
- Bootstrap: Downloads and compiles OpenSSL 3.5.x with OpenResty patches
- PCRE2 Compilation: Downloads, SHA256-verifies, and builds PCRE2 10.47 with JIT support
- Optional Module: Downloads ngx_http_proxy_connect_module if enabled
- OpenResty Build: Configures and compiles OpenResty with all modules
- LuaRocks Installation: Installs Lua package manager
- 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
-jdumpfor 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 .