Lua programs are usually distributed as source files and executed with a Lua interpreter already installed on the target system. That is convenient during development, but less convenient when a command-line tool needs to be delivered to machines without a prepared Lua environment.
luainstaller is an open-source packaging tool for this use case. It analyzes a Lua project, collects its runtime components, and produces an executable for the same platform family as the build host. Its role is broadly comparable to PyInstaller in the Python ecosystem.
The project is released under LGPL-3.0-or-later and supports the official Lua 5.1 through 5.5 implementations. LuaJIT is not supported.
What problem does it solve?
Lua already has tools such as srlua and luastatic.
srlua uses a relatively direct approach: it combines a Lua interpreter with a script. luastatic can embed Lua modules into a C launcher. Both are useful, but larger projects usually require additional work around dependency discovery, native modules, ABI compatibility, build output, and diagnostics.
luainstaller provides a more complete packaging workflow:
- static, runtime, and manual dependency discovery;
- embedding of the entry script and pure Lua modules;
- handling of discovered Lua C modules and the Lua runtime;
-
onedirdirectory bundles; - self-extracting
onefileexecutables; - manifests, logs, generated C source, and relinking material;
- both command-line and Lua library interfaces.
It does not turn Lua source code into native machine code. The generated program still runs through an included Lua interpreter. The main difference is that the target machine does not need a separate lua command installed.
Installation
luainstaller is published on LuaRocks:
luarocks install luainstaller
The installation provides two command names:
luai -v
luainstaller version
A complete official Lua environment and LuaRocks are required. Building executables also requires the matching Lua headers, runtime library, and a local C toolchain.
Lua 5.5 requires LuaRocks 3.13.0 or newer because older LuaRocks releases do not recognize the Lua 5.5 installation ABI.
Two command-line interfaces
The tool exposes the same features through two command styles.
luai uses compact options:
luai -a app/main.lua
luai -t app/main.lua
luai -b app/main.lua
luainstaller uses subcommands:
luainstaller analyze app/main.lua
luainstaller trace app/main.lua
luainstaller build app/main.lua
The two interfaces share the same implementation, but their syntax should not be mixed.
The main operations are:
-
analyze: inspect project dependencies; -
trace: show how dependencies were resolved; -
build: create an executable bundle; -
logs: inspect saved build and diagnostic logs.
A practical build sequence
The recommended process is to verify the source program first, inspect its dependencies, build a directory bundle, and only then create a single-file executable.
Start by running the original script:
lua app/main.lua
Analyze and trace its dependencies:
luai -a app/main.lua --max-deps 120
luai -t app/main.lua --max-deps 120
Build an onedir bundle:
luai -b --dir app/main.lua \
-o build/app \
--max-deps 120
On Linux and macOS, the executable may then be run as:
build/app/app
On Windows, the corresponding file is normally:
build\app\app.exe
After the directory bundle works, build the self-extracting onefile form:
luai -b --file app/main.lua \
-o build/app-onefile \
--max-deps 120
The directory form is easier to inspect because the manifest, generated source, native modules, and supporting files remain visible. The single-file form packages that directory into a self-extracting executable and unpacks it to a temporary location when launched.
For troubleshooting, the onedir result should be treated as the primary build artifact. A failure in onefile mode is easier to diagnose after the equivalent directory bundle has already been verified.
Dependency discovery modes
Lua projects do not always load modules in the same way. luainstaller therefore provides three discovery modes.
Static discovery
Static discovery is the default. It scans for literal module imports without executing the entry program:
local json = require("cjson")
This mode is predictable and suitable for projects whose dependencies are expressed through ordinary require("module") calls.
Runtime discovery
Runtime discovery executes one specific run of the program and records the pure Lua modules loaded during that run:
luai -a app/main.lua -d runtime -- profile_a input.json
Arguments after -- are passed to the entry script.
This is useful when module names depend on configuration or runtime conditions. Its limitation is equally direct: it only sees the code paths exercised by that particular run.
Manual discovery
Manual mode disables automatic scanning and lets the build specify files explicitly:
luai -b app/main.lua \
--no-depscan \
--include app/core.lua \
--include app/adapters/sqlite.lua \
-o build/app
This is appropriate when the project has an unusual loader, generated module names, or a tightly controlled distribution layout.
Lua API
The same implementation can also be called from Lua code:
local luainstaller = require("luainstaller")
local result = luainstaller.bundle({
entry = "app/main.lua",
mode = "onedir",
out = "build/app",
max_deps = 120,
})
if not result.ok then
io.stderr:write(result.error.type, ": ", result.error.message, "\n")
os.exit(1)
end
print(result.executable)
This interface is useful for custom build scripts or projects that already manage their release process in Lua.
Platform and ABI limitations
The generated executable is tied to the environment in which it was built.
The Lua interpreter used during packaging, the headers, the linked runtime, and copied Lua C modules must use the same major and minor Lua ABI. A module compiled for Lua 5.4 cannot simply be inserted into a Lua 5.3 build.
Other practical limitations include:
- builds target the platform and architecture of the build host;
-
--target-osdoes not provide true cross-compilation; - system libraries required by Lua C modules are not recursively bundled in every case;
- modules omitted from the bundle may still be loaded from the host through
LUA_PATHorLUA_CPATH; -
onefileis a self-extracting executable, not an operating-system installer; - redistribution must preserve the supplied licenses, generated source, and relinking material.
Before release, the result should be tested without a system Lua installation and with LUA_PATH and LUA_CPATH cleared. This prevents missing dependencies from being silently supplied by the development machine.
Where it fits
luainstaller is most useful for Lua command-line programs and small applications that need a repeatable distribution process.
For a single short script, shipping a Lua interpreter beside the source may still be sufficient. For a project with multiple modules, native extensions, dependency tracing, and single-file distribution requirements, a dedicated packaging workflow becomes more useful.
The source code, documentation, platform notes, and troubleshooting material are available in the GitHub repository. The packaged release and installation command are available on LuaRocks.


Top comments (0)