Check out how OpenResty XRay helps organizations troubleshoot issues and optimize the performance of their applications.

Learn More LIVE DEMO

This OpenResty tutorial series takes you from a fresh Linux server to a working Nginx + Lua application in seven hands-on steps — no prior Lua experience required.

OpenResty is a full-featured web platform that extends Nginx with a built-in LuaJIT runtime. Instead of writing C modules to customize Nginx behavior, you write Lua — and the same nonblocking event loop that makes Nginx fast also makes your Lua code fast. A single OpenResty worker process can handle tens of thousands of concurrent connections without spawning threads.

Each section below covers one milestone on the learning path, with a summary of what you will build and a link to the full hands-on article. Work through the steps in order if you are new to OpenResty; jump directly to any section if you are looking for a specific topic.

What Is OpenResty?

OpenResty packages Nginx, LuaJIT, and a set of Lua libraries into a single distribution. When a request arrives, Nginx routes it through its event-driven pipeline and, at configurable phases, hands control to a Lua coroutine. That coroutine can perform nonblocking I/O — TCP connections, DNS lookups, timers — without blocking the worker thread, because OpenResty’s cosocket API suspends the coroutine and resumes it when data is ready.

The result is an architecture where:

  • One OS thread multiplexes thousands of concurrent requests.
  • Lua code replaces C for custom logic — rate limiting, authentication, dynamic routing, response transformation.
  • LuaJIT compiles hot Lua paths to native machine code, closing the gap with hand-written C for CPU-bound work.

OpenResty is deployed on more than 40 million domains worldwide and is the foundation of products like Kong, Apache APISIX, and OpenResty Edge.

Who This Tutorial Is For

This series is written for engineers who already understand HTTP and have used Nginx as a reverse proxy or static file server. No prior Lua experience is required — each article introduces exactly the Lua needed for that step. If you are coming from a background in Python, Go, or Node.js, you will find the nonblocking model familiar; the main adjustment is that everything runs inside Nginx’s event loop rather than a standalone process.

The OpenResty Tutorial Series

The series follows a progressive learning path. Each article builds on the one before it.


Step 1 — Install OpenResty

Before writing any Lua, you need a working OpenResty installation. OpenResty provides pre-built binary packages for all major Linux distributions — installation takes under five minutes using the official APT or YUM repositories and requires no compilation.

How to Install OpenResty

For the official package repository instructions covering all supported Linux distributions, refer to the official OpenResty Linux Packages page.

After installation, verify that you are running OpenResty’s Nginx binary (not the system Nginx) by checking:

export PATH=/usr/local/openresty/nginx/sbin:$PATH
which nginx
nginx -v

Step 2 — Hello World: Your First OpenResty Application

Hello World HTTP Example in OpenResty

The first article walks through the minimum project structure needed to run an OpenResty application from scratch. You create a conf/ and a logs/ directory, write a minimal nginx.conf, embed Lua with content_by_lua_block, and verify the result with curl.

location / {
    default_type text/plain;
    content_by_lua_block {
        ngx.say("Hello World")
    }
}

By the end you will understand the nginx -p prefix convention, know how to start and stop the server, and have a reusable project skeleton for every subsequent article in this series.


Step 3 — Lua Modules: Organizing Your Application Code

How to Write Lua Modules in OpenResty

Inline content_by_lua_block code is fine for one-liners, but any real application needs reusable functions in separate files. This article introduces the _M table pattern — the standard way to declare a Lua module in OpenResty — and shows how to configure lua_package_path so that require can find your modules at runtime.

local _M = {}

function _M.greet(name)
    ngx.say("Greetings from ", name)
end

return _M

The article also covers init_by_lua_block, which preloads modules at server startup so the first real request does not pay the loading cost. This is the foundation for every subsequent article.


Step 4 — Sharing Data Between Requests

How to Share Data Between Requests in OpenResty

OpenResty workers are isolated OS processes that do not share memory by default. This article covers the two mechanisms OpenResty provides for sharing state across requests:

Lua Module Variablelua_shared_dict
ScopeSingle worker processAll worker processes
Value typesAny Lua typeString, number, boolean
Typical usePer-worker LRU cache, configCounters, rate limiting, shared flags
Atomic operationsNoYes (incr, add, set)

Understanding the difference between per-worker and cross-worker sharing is essential before building anything that tracks state — counters, rate limiters, or connection pools.


Step 5 — Streaming HTTP Responses

Streaming HTTP Response in OpenResty with ngx.flush

OpenResty’s ngx.flush(true) call is fully nonblocking: it pushes buffered output to the client and suspends the current coroutine until the write completes, freeing the worker thread to serve other requests in the meantime. This makes it straightforward to stream large responses — or implement server-sent events — without blocking.

The article demonstrates:

  • Configuring a location for chunked streaming output
  • Using ngx.flush(true) and ngx.sleep() in a loop
  • Benchmarking 500 concurrent slow-streaming connections against a single worker process (result: 120+ requests/sec, single OS thread)
  • Consuming the stream from a browser via XMLHttpRequest

Step 6 — CLI Tools: resty and restydoc

OpenResty ships two command-line utilities that accelerate day-to-day development. They are covered in separate articles because they serve distinct purposes.

The resty Command-Line Runner

OpenResty resty CLI Tutorial

resty spins up a headless Nginx behind the scenes and runs your Lua code directly from the terminal — no nginx.conf required. It is the fastest way to test a cosocket call, prototype a module function, or inspect LuaJIT compilation output.

resty -e 'ngx.say("Hello World")'
resty -I lua/ -e 'require "mymodule".run()'
resty --shdict 'cache 10m' -e 'print(ngx.shared.cache:set("k", 1))'
resty -jv bench.lua   # inspect JIT traces

Looking Up Documentation with restydoc

How to Look Up OpenResty Documentation with restydoc

restydoc brings the full OpenResty reference — Nginx directives, the ngx.* Lua API, LuaJIT extensions, and bundled Lua module docs — into your terminal without opening a browser. It is modeled after Perl’s perldoc and uses the less pager.

restydoc -s ngx.flush          # OpenResty Lua API
restydoc -s lua_shared_dict    # Nginx directive
restydoc -s string.find        # standard Lua
restydoc resty.redis           # full Lua module docs

Step 7 — Performance: Benchmarking and LuaJIT Bytecode

Benchmarking Lua Code Correctly

How to Benchmark Lua Code in OpenResty

Measuring Lua performance with time resty is misleading — the resty process startup adds around 11 milliseconds of overhead that swamps nanosecond-scale function costs. The correct approach uses ngx.now() for elapsed time, a warmup loop to ensure the function is JIT-compiled before recording starts, and collectgarbage() to clear pending GC objects.

local function target()
    ngx.re.find("hello, world.", [[\w+\.]], "jo")
end

for i = 1, 100 do target() end  -- JIT warmup

ngx.update_time()
local begin = ngx.now()
local N = 1e7
for i = 1, N do target() end
ngx.update_time()
ngx.say("elapsed: ", (ngx.now() - begin) / N)

Precompiling Lua Modules into LuaJIT Bytecode

LuaJIT Bytecode in OpenResty: How to Precompile Lua Modules

For large Lua modules, precompiling to .ljbc bytecode files cuts startup time significantly. A 1.6 MB Lua source file that took 12 milliseconds to load dropped to 2 milliseconds after precompilation — a 6× improvement. The bytecode file is also more than 50% smaller than the source.

/usr/local/openresty/luajit/bin/luajit -bg mymodule.lua mymodule.ljbc

In nginx.conf, tell OpenResty to prefer .ljbc files:

lua_package_path "$prefix/lua/?.ljbc;$prefix/lua/?.lua;;";

Note: .ljbc files are not portable across OpenResty build modes (x64 vs GC64). Always recompile bytecode from source after upgrading OpenResty.


Core Concepts Quick Reference

ConceptWhat It DoesIntroduced In
content_by_lua_blockEmbeds a Lua handler at the content phase of a locationHello World
ngx.say()Writes a line to the response bufferHello World
lua_package_pathTells require where to find .lua and .ljbc filesLua Modules
init_by_lua_blockRuns Lua once at server startup; used to preload modulesLua Modules
lua_shared_dictDeclares a named shared memory zone accessible by all workersData Sharing
ngx.shared.DICTLua API for reading and writing a shared memory dictionaryData Sharing
ngx.flush(true)Nonblocking flush of the write buffer to the clientStreaming
ngx.sleep()Yields the current coroutine without blocking the worker threadStreaming
resty -eRuns inline Lua from the terminal without a config fileresty CLI
restydoc -sLooks up a directive or API section in the terminal pagerrestydoc
ngx.now()Returns wall-clock time in seconds with millisecond precisionBenchmarking
.ljbcLuaJIT precompiled bytecode file; loaded faster than .lua sourceBytecode

Frequently Asked Questions

Is OpenResty free to use?

Yes. OpenResty is free and open-source software. It is distributed under a BSD license, the same license used by Nginx. The full source code, pre-built binary packages for Linux, and bundled Lua libraries are all freely available from openresty.org. There is no paid license required to run OpenResty in production.

What is the difference between OpenResty and Nginx?

OpenResty is a superset of Nginx. According to the official OpenResty website, OpenResty is “a dynamic web platform based on NGINX and LuaJIT.” Every valid nginx.conf runs unchanged on OpenResty, and you can add Lua incrementally — one location at a time. The difference is that OpenResty embeds LuaJIT and exposes a nonblocking Lua API — the ngx.* namespace — that lets you write custom request-handling logic without compiling a C module. If you need programmable request logic, use OpenResty. If you need a plain reverse proxy or static file server with no custom code, Nginx alone is sufficient.

What is OpenResty used for?

OpenResty is most commonly used as a programmable API gateway, a Web Application Firewall (WAF), a reverse proxy with custom authentication or routing logic, and a CDN edge node. Because Lua runs inside Nginx’s event loop at nanosecond latency, it is well suited for any task that would otherwise require a sidecar process or a round-trip to an external service — token validation, rate limiting, request rewriting, and A/B routing are typical examples.

What version of Lua does OpenResty use?

OpenResty uses LuaJIT 2.x, which implements the Lua 5.1 language with a small set of Lua 5.2 compatibility extensions. OpenResty maintains its own fork of LuaJIT with additional patches for correctness and performance. Standard Lua 5.1 code runs without modification; LuaJIT-specific features such as the FFI and bit library are also available.

Is OpenResty hard to learn if I already know Nginx?

The Nginx configuration side is identical. What is new is the Lua layer: the ngx.* API, the coroutine-based concurrency model, and the concept of Nginx request phases (access_by_lua_block, content_by_lua_block, log_by_lua_block, etc.). This tutorial series introduces each concept at the point where it is first needed, so prior Lua experience is not required to follow along.

Is there an enterprise version of OpenResty?

Yes. OpenResty Edge is the commercial product built on top of OpenResty. It bundles a private CDN, universal gateway, WAF, DDoS protection, and Kubernetes integration into a single on-premises platform — managed through a web UI rather than hand-edited nginx.conf files. OpenResty Edge is developed and supported by OpenResty Inc., the company founded by OpenResty’s creator.

How do I debug Lua errors in OpenResty?

Lua runtime errors are written to Nginx’s error log. The first place to look is:

tail -f logs/error.log

For more structured debugging, restydoc -s ngx.log documents the logging API. The resty command-line utility is also useful for isolating and reproducing a bug outside of a running server.

What’s Next Beyond This Series

After completing the seven steps above, you will have the foundation to build production OpenResty applications. Topics to explore next include:

  • resty.lrucache — a per-worker LRU cache module for hot data that does not need cross-worker sharing
  • Cosocket connectionsngx.socket.tcp() for nonblocking connections to Redis, MySQL, PostgreSQL, and other backends
  • ngx.timer.at and ngx.timer.every — background timers for periodic tasks outside the request cycle
  • OpenResty XRay — a non-invasive dynamic tracing tool for profiling and troubleshooting running OpenResty applications without code changes

If you like this tutorial series, subscribe to this blog and our YouTube channel.

About The Author

Yichun Zhang (Github handle: agentzh), is the original creator of the OpenResty® open-source project and the CEO of OpenResty Inc..

Yichun is one of the earliest advocates and leaders of “open-source technology”. He worked at many internationally renowned tech companies, such as Cloudflare, Yahoo!. He is a pioneer of “edge computing”, “dynamic tracing” and “machine coding”, with over 22 years of programming and 16 years of open source experience. Yichun is well-known in the open-source space as the project leader of OpenResty®, adopted by more than 40 million global website domains.

OpenResty Inc., the enterprise software start-up founded by Yichun in 2017, has customers from some of the biggest companies in the world. Its flagship product, OpenResty XRay, is a non-invasive profiling and troubleshooting tool that significantly enhances and utilizes dynamic tracing technology. And its OpenResty Edge product is a powerful distributed traffic management and private CDN software product.

As an avid open-source contributor, Yichun has contributed more than a million lines of code to numerous open-source projects, including Linux kernel, Nginx, LuaJIT, GDB, SystemTap, LLVM, Perl, etc. He has also authored more than 60 open-source software libraries.