LuaJIT Bytecode in OpenResty - How to Precompile Lua Modules
LuaJIT bytecode is the pre-parsed, binary form of a Lua module that OpenResty’s LuaJIT virtual machine can load directly, skipping the source-parsing step that would otherwise happen on every startup. OpenResty ships with its own maintained branch of LuaJIT — one key consequence is that bytecode compiled in different build modes (GC64 and x64) is not interchangeable between OpenResty versions. For large Lua modules, precompiling to a .ljbc file both reduces startup latency and produces a smaller file than the original Lua source.
This tutorial demonstrates how to pre-compile Lua modules into LuaJIT bytecode. This can help reduce the startup time of an OpenResty application.
Measuring the Startup Cost of Loading a Lua Module
export PATH=/usr/local/openresty/bin:$PATH
cd ~
mkdir -p precomp
cd precomp/
Here we will use a big Lua module file named pkg-stap.lua. It was generated by our opslang compiler.
cp ~/git/opslang/pkg-stap.lua ./
ls -lh *.lua
We can see that this Lua module is 1.6MB.
Let’s try loading this Lua module with the resty utility.
time resty -I. -e 'require "pkg-stap"'
It takes 23 milliseconds in total.
Let’s check the original overhead of running an empty Lua program.
time resty -e ''
It is about 11 milliseconds. So loading the module itself takes about 12 milliseconds.
For a broader look at benchmarking Lua code in OpenResty, see Measuring Lua Execution Time in OpenResty.
How to Precompile a Lua Module Using luajit -b
Let’s try precompiling the Lua module into LuaJIT bytecode.
time /usr/local/openresty/luajit/bin/luajit -bg pkg-stap.lua pkg-stap.ljbc
Here we use OpenResty’s luajit program.
The .ljbc File Extension and How OpenResty Loads It
It generates a LuaJIT bytecode file with the .ljbc file extension.
ls -lh *.ljbc
It’s fun to see the bytecode file is also more than 50% smaller!
Then try loading it with resty again.
time resty -I. -e 'require "pkg-stap"'
Just 13 milliseconds in total!
It’s now almost like loading an empty Lua program! Only about 2 milliseconds extra.
time resty -e ''
The resty utility always tries to load an .ljbc file before a .lua file when loading a module.
Using LuaJIT Bytecode in an OpenResty Server
Let’s see how to make it work for an OpenResty server.
mkdir conf logs lua
Copy our Lua module files to the lua/ subdirectory.
mv *.lua *.ljbc lua/
tree .
Write a simple nginx configuration file, conf/nginx.conf. We make the following edits:
- Use a single worker process.
- Use 1024 worker connections.
- In the
lua_package_pathdirective, it is important to try.ljbcfiles before.luafiles. You can also try keeping the.ljbcfiles only to be sure. - Inside
init_by_lua_block, we preload our modules so that any module loading failures can be caught upon server startup. This also leads to faster first requests, and smaller memory footprint due to theCOWoptimization.
worker_processes 1;
events {
worker_connections 1024;
}
http {
lua_package_path "$prefix/lua/?.ljbc;$prefix/lua/?.lua;;";
init_by_lua_block {
require "pkg-stap"
}
server {
listen 8080;
location / { return 200 "ok\n"; }
}
}
Check the directory tree.
tree .
Looking good!
Try testing the server configuration using the -t option.
time openresty -p $PWD/ -t
Start it up.
time openresty -p $PWD/
About 7 milliseconds.
Try removing the LuaJIT bytecode file.
rm lua/*.ljbc
Stop the server.
kill -QUIT `cat logs/nginx.pid`
Start the server again.
time openresty -p $PWD/
This time it is 17 milliseconds! We can see it is 10 milliseconds slower too by loading the Lua source file.
Finally, try removing the Lua source files too.
rm lua/*.lua
Stop the server and start it again.
time openresty -p $PWD/
This time, we get an error as expected since both versions of the module are gone.
When Precompiling Is and Is Not Worth It
For small Lua module files, it is already very fast to load their source code.
echo 'local _M = {} function _M.foo() end return _M' > a.lua
ls -l a.lua
time resty -I. -e 'require "a"'
Precompiling a small Lua module file won’t help much.
time /usr/local/openresty/luajit/bin/luajit -bg a.lua a.ljbc
time resty -I. -e 'require "a"'
Indeed not much. But the time saving can accumulate quickly if you have many small modules to load up.
LuaJIT Bytecode Compatibility Between OpenResty Versions
Not all .ljbc files are interchangeable. As documented in the LuaJIT GC64 Mode article, bytecode compiled in LuaJIT’s x64 build mode is incompatible with bytecode from the GC64 build mode. OpenResty switched to GC64 by default starting with the 1.15.8.1 release — so .ljbc files compiled against an older x64 OpenResty installation will not load correctly on a newer GC64 one. Always recompile your .ljbc files from source after upgrading OpenResty.
Frequently Asked Questions
Does LuaJIT bytecode make my OpenResty application run faster at runtime?
Precompiling to LuaJIT bytecode reduces startup time only, not runtime execution speed. As this tutorial shows, loading a 1.6 MB Lua module from source took about 12 milliseconds, while the precompiled .ljbc version took only about 2 milliseconds. Once loaded, OpenResty’s LuaJIT processes both the source-compiled and precompiled versions through the same JIT pipeline — the savings are entirely in the loading phase.
What is the .ljbc file extension?
.ljbc is the file extension for LuaJIT bytecode files produced by OpenResty’s luajit -bg command. When lua_package_path is set to search .ljbc before .lua — for example, "$prefix/lua/?.ljbc;$prefix/lua/?.lua;;" — both the resty utility and the OpenResty server will automatically prefer the precompiled file. If no .ljbc file is found, OpenResty falls back to loading the .lua source transparently.
Is a .ljbc file compiled on one OpenResty version usable on another?
Not necessarily. OpenResty’s LuaJIT bytecode is incompatible between its x64 and GC64 build modes, as documented in the LuaJIT GC64 Mode article. Since OpenResty switched to GC64 by default in the 1.15.8.1 release, .ljbc files compiled on an older x64 installation may fail to load on a newer one. Always recompile .ljbc files from source after an OpenResty version upgrade.
Should I precompile every Lua module in my OpenResty project?
Only large modules show a meaningful startup improvement. As this tutorial demonstrates, small modules are already loaded quickly from source, and the per-file saving is negligible. The most effective pattern is to precompile your largest modules and preload them in init_by_lua_block — this combines the faster bytecode loading with the memory savings from Nginx’s copy-on-write optimization across worker processes.
Continue the OpenResty Tutorial Series
This article is part of the OpenResty Tutorial Series.
| ← Prev | Measuring Lua Execution Time in OpenResty |
| You are here | LuaJIT Bytecode in OpenResty: How to Precompile Lua Modules |
| ↑ Series Index | OpenResty Tutorial Series |
If you like this tutorial, explore the full OpenResty Tutorial Series, subscribe to this blog, and follow our YouTube channel. Thank you!
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.
































