Dependency Management Patterns in Lua
Lua has no built-in package manager. That means dependency management is a question every Lua project eventually has to answer. The right choice depends on whether you’re writing a game plugin, a server application, or a library you’re distributing to others.
The Options
- Luarocks — Lua’s community package manager
- Git submodules — include a library as a subdirectory in your project
- Inline bundling — copy the library code directly into your project
- Manual download — fetch and place the library files yourself
Each approach has tradeoffs in reliability, portability, and maintenance burden.
Using Luarocks
Luarocks is the closest thing Lua has to a standard package manager. It installs packages to a user-configured path and lets you require them by name.
# Install a package
luarocks install luasocket
# Install to a specific prefix
luarocks install --local luasocket
To use a locally installed rock, add the path to package.cpath or use luarocks paths in your script:
luarocks path --bin
# Adds /home/user/.luarocks/share/lua/5.1 to LUA_PATH
In your code:
local socket = require("socket")
Luarocks handles versioning and dependency resolution. The catch: you need luarocks installed on every machine that runs your project, and not every library is available as a rock.
Git Submodules
If you need a specific version of a library and want it tracked in your repo, git submodules work well:
# Add a library as a submodule
git submodule add https://github.com/example/lua-library.git vendor/library
In your code, adjust the path:
package.path = package.path .. ";./vendor/library/?.lua"
local lib = require("library")
Submodules pin you to a specific commit, which is good for reproducibility. But they add friction to updates and can cause issues when collaborators clone a fresh copy.
Inline Bundling
For distribution or self-contained projects, many developers copy dependencies into a lib/ directory:
myproject/
lib/
sockets.lua
lpeg.lua
main.lua
package.path = package.path .. ";./lib/?.lua"
local socket = require("sockets")
The advantage: everything lives in one repo, no external dependencies. The downside: updating means manually replacing files, and you lose version history for the library.
This approach is common in Roblox plugins and game scripts where external package managers aren’t available.
Writing Your Own require Handler
For more control, you can implement a custom module loader:
local function add_search_path(path)
package.path = package.path .. ";" .. path .. "/?.lua"
package.path = package.path .. ";" .. path .. "/?/init.lua"
end
-- Add project-local lib directory
add_search_path("./lib")
-- Add vendor directory
add_search_path("./vendor")
This gives you fine-grained control over load order and lets you handle missing modules gracefully.
Detecting Installed Packages
Before requiring an optional dependency, check if it’s available:
local function package_available(name)
local ok, err = pcall(require, name)
return ok
end
if package_available("luasocket") then
local socket = require("socket")
-- use socket
else
-- fallback or error
end
This pattern is how many libraries support optional dependencies without forcing them on users.
Working with OpenResty and LuaRocks
OpenResty has its own module path hierarchy:
-- OpenResty loads from these paths automatically
package.path = package.path .. ";/usr/local/openresty/lua/?.lua"
If you need a rock in OpenResty, install it to the OpenResty path:
luarocks --lua-version=5.1 install luasocket
Or configure the path explicitly:
luarocks configlua_path /usr/local/openresty/lua/?.lua
vendoring Libraries
A common pattern is to vendor libraries — copy them into your project and load them from a local path:
-- lib/mylib.lua
local mylib = {}
function mylib.foo()
return "foo"
end
return mylib
-- main.lua
package.path = package.path .. ";./lib/?.lua"
local mylib = require("mylib")
Vendoring gives you a stable, reproducible copy that works without any package manager installed.
When to Use What
| Approach | Best for |
|---|---|
| Luarocks | Server apps, CI environments, libraries you distribute |
| Git submodules | Projects where you want version-pinned dependencies |
| Inline bundling | Games, plugins, single-file distributions |
| Custom require | When you need specific load order or fallbacks |
See Also
- /guides/lua-luarocks-guide/ — installing and using luarocks
- /tutorials/modules-and-require/ — how require works and how to structure modules
- /guides/lua-plugin-architectures/ — patterns for organizing plugin code with dependencies