Skip to content

Building a binary

Like Go, a Ludi application can ship as one self-contained executable — no Lua, no LuaRocks on the target machine:

Terminal window
ludi build # entry auto-detected when unambiguous
ludi build server.lua -o api # or explicit entry and output name
./api

The build is instant: no compiler runs. ludi build packs every *.lua file under the current directory into a copy of the ludi runtime — a static Lua 5.5, the framework and your code in a single file. Hidden directories, target/ and lua_modules/ are excluded, the same rules the hot-reload watcher uses.

At run time, require resolves from inside the bundle; the filesystem is never consulted for modules. Deploy is scp and run.

Without an explicit entry, ludi build accepts the conventional names app.lua, server.lua, main.lua and init.lua — but only when exactly one of them exists in the project. With two or more present there is no safe guess (an app.lua next to a server.lua is usually a module, not the runner), so the build stops and asks:

ludi build: several possible entrypoints here (app.lua, server.lua); pass one: ludi build <entry.lua>

Any file name works when passed explicitly: ludi build whatever.lua.

Each GitHub release ships the CLI per platform — download, chmod +x, done:

Terminal window
curl -LO https://github.com/Ludi-Framework/ludi/releases/latest/download/ludi-linux-x86_64
chmod +x ludi-linux-x86_64 && sudo mv ludi-linux-x86_64 /usr/local/bin/ludi

macOS (Apple Silicon): use ludi-darwin-arm64. Or build from source with make cli in the repository (requires Rust).

  • A bundled binary always runs Lua 5.5, whatever interpreter you develop with.
  • Command-line arguments reach the app as usual (arg[1], arg[2], …).
  • LUDI_WATCH is ignored inside a bundle — there is nothing on disk to watch. Hot reload is for development; the binary is for deploy.