Need help with luvi?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

243 Stars 54 Forks Apache License 2.0 744 Commits 7 Opened issues


A project in-between luv and luvit.

Services available


Need anything else?

Contributors list


Linux Build Status Windows Build status Code Quality: Cpp Total Alerts

A project in-between luv and luvit.

The goal of this is to make building luvit and derivatives much easier.


Luvi has a somewhat unique, but very easy workflow for creating self-contained binaries on systems that don't have a compiler.

# Make a folder
git init myapp
# Write the app
vim myapp/main.lua
# Run the app
luvi myapp
# Build the binary when done
luvi myapp -o mybinary
# Run the new self-contained binary
# Deploy / Publish / Profit!

Main API


is run in a mostly stock luajit environment with a few extra things added. This means you can use the luajit extensions including
features, which we turn on.

Libuv is baked in

The "uv" module contains bindings to libuv as defined in the luv project. Simply

to access it.

Use this for file I/O, network I/O, timers, or various interfaces with the operating system. This lets you write fast non-blocking network servers or frameworks. The APIs in luv mirror what's in libuv allowing you to add whatever API sugar you want on top be it callbacks, coroutines, or whatever.

Just be sure to call
and the end of your script to start the event loop if you want to actually wait for any events to happen.
local uv = require('uv')

local function setTimeout(timeout, callback) local timer = uv.new_timer() local function ontimeout() print("ontimeout", self) uv.timer_stop(timer) uv.close(timer) callback(self) end uv.timer_start(timer, timeout, 0, ontimeout) return timer end

setTimeout(1000, function () print("This happens later") end)

print("This happens first")

-- This blocks till the timer is done

Integration with C's main function

The raw

from C side is exposed as a zero indexed lua table of strings at
print("Your arguments were", args)

The "env" module provides read/write access to your local environment variables via

, and
local env = require('env')

-- Convert the module to a mutable magic table. local environment = setmetatable({}, { __pairs = function (table) local keys = env.keys() local index = 0 return function (...) index = index + 1 local name = keys[index] if name then return name, table[name] end end end, __index = function (table, name) return env.get(name) end, __newindex = function (table, name, value) if value then env.set(name, value, 1) else env.unset(name) end end }))

If you return an integer from

it will be your program's exit code.

Bundle I/O

If you're running from a unzipped folder on disk or a zipped bundle appended to the binary, the I/O to read from this is the same. This is exposed as the

property in the "luvi" module.
local bundle = require("luvi").bundle
local files = bundle.readdir("")


Load metadata about a file in the bundle. This includes

("file" or "directory"),
(in ms since epoch), and
(in bytes).

If the file doesn't exist, it returns



Read a directory. Returns a list of filenames in the directory.

If the directory doesn't exist, it return



Read the contents of a file. Returns a string if the file exists and

if it doesn't.


There is also a "utils" module that has some useful debugging stuff like a colorized pretty printer.

local uv = require('uv')
local dump = require('utils').dump
-- Create a global p() function that pretty prints any values
-- to stdout using libuv's APIs
_G.p = function (...)
    local n = select('#', ...)
    local arguments = { ... }

for i = 1, n do
    arguments[i] = dump(arguments[i])

local toWrite = table.concat(arguments, "\t") .. "\n"
uv.write(stdout, toWrite);


Building from Source

We maintain several binary releases of luvi to ease bootstrapping of lit and luvit apps.

The following platforms are supported:

  • Windows (x86_64 / i386)
  • FreeBSD 10.1 (x86_64)
  • Raspberry PI Raspbian (armv6)
  • Raspberry PI 2 Raspbian (armv7)
  • Debian 9 "Stretch" (x86_64)
  • OSX 10.14 "Mojave" (x86_64)

If you want to not wait for pre-built binaries and dive right in, building is based on CMake and is pretty simple.

Build Dependencies

  • Git
  • CMake
  • A C Compiler (visual studio on Windows)
  • Perl (required for OpenSSL)
  • NASM (required for OpenSSL ASM optimizations on Windows)

First clone this repo recursively.

git clone --recursive

Then run the makefile inside it. (Note this assumes you have cmake in your path.) If you're on windows, there is a

file that works mostly like the unix

Prior to building the

binary you must configure the version of
that you want to build. Currently there are three versions:
  • tiny: only the necessities; omits OpenSSL, LPeg, and lrexlib
  • regular: the normal luvit experience; includes OpenSSL, lpeg and lrexlib
  • regular-asm: includes OpenSSL's ASM optimizations
cd luvi
make regular
make test

When that's done you should have a shiny little binary in

$ ls -lh build/luvi
-rwxr-xr-x 1 tim tim 948K Nov 20 16:39 build/luvi


Run it to see usage information:

$ luvi -h

Usage: luvi bundle+ [options] [-- extra args]

bundle Path to directory or zip file containing bundle source. bundle can be specified multiple times to layer bundles on top of eachother. --version Show luvi version and compiled in options. --output target Build a luvi app by zipping the bundle and inserting luvi. --main path Specify a custom main bundle path (normally main.lua) --help Show this help file. -- All args after this go to the luvi app itself.


Run an app from disk, but pass in arguments

luvi path/to/app -- app args

Run from a app zip

luvi path/to/

Run an app that layers on top of luvit

luvi path/to/app path/to/luvit

Bundle an app with luvi to create standalone

luvi path/to/app -o target ./target some args

Run unit tests for a luvi app using custom main

luvi path/to/app -m tests/run.lua

You can run the sample repl app by doing:

build/luvi samples/

Ot the test suite with:

build/luvi samples/

CMake Flags

You can use the predefined makefile targets if you want or use cmake directly for more control.

WithOpenSSL (Default: OFF)      - Enable OpenSSL Support
WithOpenSSLASM (Default: OFF)   - Enable OpenSSL Assembly Optimizations
WithSharedOpenSSL (Default: ON) - Use System OpenSSL Library
                                  Otherwise use static library

OPENSSL_ROOT_DIR - Override the OpenSSL Root Directory OPENSSL_INCLUDE_DIR - Override the OpenSSL Include Directory OPENSSL_LIBRARIES - Override the OpenSSL Library Path

Example (Static OpenSSL):

cmake \
    -DWithOpenSSL=ON \
    -DWithSharedOpenSSL=OFF \

Example (Shared OpenSSL):

cmake \
    -DWithSharedOpenSSL=ON \
    -DWithOpenSSL=ON \
    -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl \
    -DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl/include \
    -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib \

Holy Build

Executables across Linux distributions are not largely portable for various differences. We can leverage the holy-build-box to create a portable executable for i686 and x86_64 environments.

Note: If you are attempting this on OSX, please install GNU tar from homebrew:

brew install gnu-tar

To get started:

  1. Create a docker machine:

    docker-machine create --driver vmwarefusion --vmwarefusion-cpu-count 3 holy-build-box
    eval $(docker-machine env holy-build-box)
  2. Start the build

    make linux-build
  3. Results should be the current working directory.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.