Using LuaRocks to install packages in the current directory

How to configure where LuaRocks installs modules

LuaRocks is the package manager for Lua. When you decide to install a package (or module1) there are a few places it can be installed.

The directory that LuaRocks installs to is called a tree. It’s a specially structured directory that contains all the installed module files, along with metadata about those modules.

A common request I've heard is “I wish LuaRocks worked like npm and let me install modules in the current directory.” I'm glad to report it already can! I've written this guide to clear up any confusion about how and where LuaRocks installs modules.

There are three ways of choosing where packages get installed:

  • Global — the default, system level (eg. /usr/share/lua/5.1)
  • Local — your home directory, with --local
  • Custom — any directory you specify with --tree some/directory

For general purpose modules, ones that will be available for any Lua script you execute, the local tree should be used. This tree is located in your home directory (~/.luarocks). A global install is not recommended because it requires root access.

If you're building a project in Lua you'll probably want to more closely control the dependencies and their versions. For this scenario I recommend using a tree that is located in the project’s directory. This is the approach used by other package mangers such as npm.

How Lua finds packages

This section is not specific to LuaRocks, but LuaRocks utilizes this system to provide Lua a way to load the modules it installs.

When executing require("hello.world") Lua must figure out where the file for this module is in order to load it. A module can resolve to either Lua source code, a .lua file, or a native code library, a .so on Linux or .dll on Windows.

Similar to your operating system’s PATH environment variable for finding executables, Lua has a path to find modules. As mentioned above, there are two types of modules, so Lua has two paths.

You can view (and edit) the paths using package.path and package.cpath within a Lua script:

print(package.path) -- where .lua files are searched for
print(package.cpath) -- where native modules are searched for

-- add a new directory to the path
package.path = package.path .. ";/opt/custom/?.lua"

require("hello.zone") -- might load /opt/custom/hello/zone.lua

The initial value of package.path and package.cpath comes from the defaults compiled into your Lua executable, and the special environment variables LUA_PATH and LUA_CPATH.

Setting the path with LuaRocks

LuaRocks has a built in command for setting the Lua path environment variables. It’s called luarocks path

Running it the command might produce:

$ luarocks path
export LUA_PATH='/home/leafo/.luarocks/share/lua/5.1/?.lua;/home/leafo/.luarocks/share/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua;;./?.lua;/usr/lib/lua/5.1/?.lua;/usr/lib/lua/5.1/?/init.lua'
export LUA_CPATH='/home/leafo/.luarocks/lib/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;;./?.so;/usr/lib/lua/5.1/loadall.so'

The special syntax ;; can be used in the environment variable’s value to represent the default value provided by the Lua runtime.

This is a shell script that will set the Lua path environment variables to correctly load modules from the global and local install locations of LuaRocks.

You might want to append this to your .bashrc like this:

$ luarocks path >> ~/.bashrc

Now you can require() any modules that you've installed locally or globally without any additional steps. (After restarting your shell or sourcing the rc file)

Modules installed globally will work without adjusting the Lua path since they are installed in the default Lua system path.

The install locations

Using a custom directory

Don’t care about the explanation, go to the quick guide.

Installing packages to the current directory is as simple as

luarocks install --tree lua_modules lpeg

This will install the package lpeg (and any dependencies if necessary) to the directory lua_modules in the current directory.

Loading those modules is a bit more complicated.

The structure of the tree, at lua_modules/, after installing lpeg looks like this:

lua_modules/
├── lib
│   ├── lua
│   │   └── 5.1
│   │       └── lpeg.so
│   └── luarocks
│       └── rocks-5.1
│           ├── lpeg
│           │   └── 1.0.0-1
│           │       ├── lpeg-1.0.0-1.rockspec
│           │       └── rock_manifest
│           └── manifest
└── share
    └── lua
        └── 5.1
            └── re.lua

I happen to be using a version of LuaRocks compiled for Lua 5.1 in this example, but this technique will work for any version. Just make sure the paths you create have the correct version

For this example I chose lpeg because it contains both a .lua module: re, and a native .so module: lpeg. From this we can already see how we might structure our Lua path and cpath.

  • The path would be: lua_modules/share/lua/5.1/?.lua
  • The cpath would be: lua_modules/lib/lua/5.1/?.so

There’s one more entry we'd like to add to the path. A common idiom is to use an init.lua file as the entry point for a package, located in the directory of that package. We'll amend the path:

  • The path would be: lua_modules/share/lua/5.1/?.lua;lua_modules/share/lua/5.1/?/init.lua
  • The cpath would be lua_modules/lib/lua/5.1/?.so

; is used to provide two places to look in the path

As discussed above there are a few ways to enable these paths. From command line you could prepend the environment variables in front of your command:

LUA_PATH='lua_modules/share/lua/5.1/?.lua;lua_modules/share/lua/5.1/?/init.lua;;' LUA_CPATH='lua_modules/lib/lua/5.1/?.so' lua my_script.lua

That’s a mouthful though, so lets use an alternate approach. The lua command line executable can take an -l flag to specify a module to load before executing the script.

Lets create a new file in the current directory, set_paths.lua:

-- set_paths.lua
local version = _VERSION:match("%d+%.%d+")
package.path = 'lua_modules/share/lua/' .. version .. '/?.lua;lua_modules/share/lua/' .. version .. '/?/init.lua' .. package.path
package.cpath = 'lua_modules/lib/lua/' .. version .. '/?.so' .. package.path

Now to run a script with the modules installed in that directory:

lua -l set_paths my_script.lua

This requires that the script be run from the same directory that set_paths.lua and lua_modules/ is located

You might be tempted to put require("set_paths") in your code’s entry point to avoid the additional argument, but it’s a bad idea.

A module should run on any machine it is installed to regardless of the package path configuration. The set_paths script is only to facilitate development and execution in the current directory.

By configuring paths on script execution, you avoid embedding any assumptions about the package path in your code. If this additional step is too much to type, consider a Makefile that includes some of your common commands.

Quick guide

Want the full explanation? Scroll up.

Installing a package
luarocks install --tree lua_modules lpeg
Running scripts with packages

Create file set_paths.lua:

-- set_paths.lua
local version = _VERSION:match("%d+%.%d+")
package.path = 'lua_modules/share/lua/' .. version .. '/?.lua;lua_modules/share/lua/' .. version .. '/?/init.lua' .. package.path
package.cpath = 'lua_modules/lib/lua/' .. version .. '/?.so' .. package.path

Execute scripts:

lua -l set_paths my_script.lua

Running tests with busted:

busted --helper=set_paths

Do not include require("set_paths") in your code, scroll up to see why.

Local install

Installing packages to the local tree, or the home directory, requires the --local flag to be passed to the luarocks install install command. This is the recommended way to install packages that are generally available because it does not require root access to perform the installation.

$ luarocks install --local moonscript

The LUA_PATH and LUA_CPATH should be configured. This can be done with the luarocks path command as discussed above.

print(require("moonscript"))

Global install

Installing packages in the system tree requires root access. Prefixing the luarocks command with sudo is a common way to install global packages:

$ sudo luarocks install moonscript

Installing globally is not recommended since it requires root access. Use the local tree to install packages and make them available for all your scripts.


1: The distinction isn’t always clear, but a package is synonymous with a library, and a module is an individual thing (.lua file, shred library) you can include from that package. I tend to use them interchangeably.

Related projects

A community LuaRocks hosting repository