# Lunatik runs Lua scripts inside the Linux kernel itself

> Lunatik is a framework for scripting the Linux kernel with Lua, where the script runs in kernel space and reaches character devices, Netfilter, XDP, kprobes, fsnotify, sockets, netlink and eBPF maps through Lua modules. A command line tool loads and manages those scripts from user space, and a character device driver can be twenty lines of Lua, which is either the point or the problem depending on your kernel team.

**luainkernel/lunatik** — Lunatik is a framework for scripting the Linux kernel with Lua.

- Repository: https://github.com/luainkernel/lunatik
- Stars: 608 · Forks: 64
- Language: Shell
- License: GPL-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/luainkernel-lunatik

## Scripts that run in kernel space

Lunatik is a framework for scripting the Linux kernel with Lua, and the README is direct about where the scripts run: inside the kernel.

From there they reach the kernel's facilities through Lua modules, and the list is long: character devices, Netfilter, XDP and TC hooks, kprobes, fsnotify, sched_ext, eBPF maps, sockets, netlink, crypto, kernel threads and more. Two interfaces sit on top of that. A command line tool loads and manages the scripts from user space, and a C API lets kernel modules do the same.

The supported platform is Linux 6.6 and later, the documentation lives at luainkernel.github.io/lunatik, and the project asks contributors to join the Matrix room. The releases move in step with the kernel era: v5.1 on 2026-10-12, v5.0 on 2026-10-05 and v4.4.1 on 2026-06-21, with the last push on 2026-10-20.

What you are adopting is a Lua runtime inside the kernel, which is the single most important fact about the project. Everything else, the modules, the user space tool, the C API, follows from that.

## A character device in twenty lines of Lua

The README's worked example is a character device driver that generates random ASCII printable characters, and the whole thing fits on a screen.

```Lua
-- /lib/modules/lua/passwd.lua
--
-- implements /dev/passwd for generate passwords
-- usage: $ sudo lunatik run passwd
--        $ head -c <width> /dev/passwd

local device = require("device")
local linux  = require("linux")
local stat   = require("linux.stat")

local driver = {name = "passwd", mode = stat.IRUGO}

function driver:read() -- read(2) callback
	-- generate random ASCII printable characters
	return string.char(linux.random(32, 126))
end

-- creates a new character device
device.new(driver)
```

The structure is the C driver structure with a Lua body. The driver is a table with a name and a mode, `read` is the read callback, and `device.new` creates the character device. The mode is taken from `linux.stat`, so the file permissions are the ordinary Linux ones.

The README then explains the lifecycle. After `sudo make install` you save the file as `/lib/modules/lua/passwd.lua`, as root. Then `sudo lunatik run passwd` creates `/dev/passwd`, `head -c 16 /dev/passwd` reads a password from it, and `sudo lunatik stop passwd` removes it.

That last sentence is the operational point. The device exists only while the script is running, which means the script is the supervisor, not systemd.

## Four commands to a kernel-side REPL

The getting started section is the whole installation story:

```sh
git clone --depth 1 --recurse-submodules https://github.com/luainkernel/lunatik.git
cd lunatik && sudo make btf_install && make && sudo make install
sudo lunatik    # a REPL whose lines run in the kernel
```

Four steps, and two details carry the weight. The clone uses `--recurse-submodules`, because the Lua and Klibc sources come in as submodules and the build needs them. And `sudo make btf_install` comes first, before the build, because the module needs BTF, the kernel's type information, and the make target installs it into the running kernel's build directory.

The last command is the one people remember. `sudo lunatik` starts a read-eval-print loop whose lines run in the kernel, so you can type a line of Lua and have it execute in kernel space, with no compile step in between. That is the fastest possible feedback loop for kernel side scripting, and it is also the sharpest edge: there is no type checker between you and the kernel.

The guide also covers dependencies, OpenWRT and the first steps, and the documentation is organised as a sequence of guide files, starting with Getting started and Running scripts.

## The guide tells you what differs from userspace Lua

The documentation list is a map of the questions you will have, and two entries are the ones to read first.

Lua in the kernel is a section on what differs from userspace Lua, and it is the one to read before writing anything. Running scripts covers the command line tool, the execution contexts and the `lunatic` command. Per-CPU scripts covers one runtime per CPU, and where state belongs, which is the question that decides whether a script is correct on a machine with many cores.

Then there is a section on examples, described as device drivers, packet filters, probes and filesystem guards, which are the four shapes the project is really aimed at, and a Lua API reference plus the C API document for the kernel module side. A final section covers development and resources, with tests, contributing, talks and papers.

The repository itself mirrors that structure. The C sources are split by concern, with `lunatik_core.c`, `lunatik_run.c`, `lunatik_percpu.c` and `lunatik_val.c` alongside the headers, plus `lunatik.h`, `lunatik_aux.c`, `lunatik_cfi.h`, `lunatik_conf.h`, `lunatik_lock.h` and `lunatik_obj.c`. There is a `driver.lua` at the root, an `autogen.lua` with an `autogen/` directory for generating Lua bindings, a `Kbuild` and `Kconfig` for the kernel build, a `config.ld` for the linker, `bin/`, `lib/`, `include/`, `tools/`, `tests/` and `examples/`, and the `lua` and `luac` directories holding the interpreter sources, along with a `REUSE.toml` and a `.luarc.json` for the Lua language server.

## The examples directory is the feature list

There are more than twenty example directories, and their names describe the practical territory of the project better than the feature list does.

There are packet and network examples: `filter/`, `dnsblock/`, `dnsdoctor/`, `netfailover/`, `ifquarantine/`, `linkflap/`, `sniclassify/`, `systrack/`, `tap/`, `tcpreject/` and `lldpd/`, which is a link layer neighbour discovery daemon. There are device and input examples: `echod/` for an echo device, `gesture/` for input gestures, `cpuexporter/` for exporting CPU information, `systrack/` for system tracing, and `dropreason/` for reporting why something was dropped.

There are monitoring and security examples: `fsmonitor/` for filesystem monitoring, `execguard/` for guarding program execution, `keylocker/` for key handling, `spyglass/` for observing traffic.

And there is one that stands out: `xiaomi/`, which is the hardware vendor at the heart of the project's origin, and `common/` and `shared/` for the pieces those examples share.

The set tells you what the framework is good at: reacting to events and enforcing policy, on packets, on files, on processes and on input devices. That is the shape of a Lua script, and it is also the boundary of one.

## What the Makefile reveals about the build

The Makefile is where the engineering decisions sit, and it is unusually informative.

The module installs against the running kernel, with `KERNEL_RELEASE ?= ${shell uname -r}`, `MODULES_PATH := /lib/modules` and a release specific path under it. BTF has its own install path inside the kernel build directory, and the module ends up under the kernel's `kernel` directory, while the Lua scripts are installed to `/lib/modules/lua` and the user space tool to `/usr/local/sbin`. There is a separate path for eBPF artefacts at `/usr/local/lib/bpf/lunatik` and one for the installed tests at `/usr/local/share/lunatik/tests`.

The interpreter sources are named in the make file, with the core Lua modules listed and built from the `lua/` directory, and the compiler `luac` is built with the host compiler from the same `lua/` sources and the same `_KERNEL` configuration as `lunatik.ko`. The flags are `-std=gnu99 -O2 -Wall -D_KERNEL -DLUA_USE_LINUX`.

Two details are worth flagging for a reviewer. The `LUA ?= lua5.5` variable means the build expects a Lua 5.5 host interpreter for the tooling, and the `BYTECODE=1` option installs kernel Lua scripts as stripped chunks under their `.lua` names, so the scripts can be shipped as bytecode rather than source.

A `MOONTASTIK_RELEASE ?= v0.1c` variable is also present, which points at a companion project the make file expects to exist at a particular release.

## Dual licensing, and the question it answers

Lunatik is dual-licensed under MIT or GPL-2.0-only, and the submodules have their own terms. The Lua submodule is licensed under MIT, and the Klibc submodule is dual-licensed under BSD 3-Clause or GPL-2.0-only.

The dual licence is not a detail for a kernel component. A kernel module that is linked into the kernel, or distributed alongside it, is in the GPL's orbit by design, and the MIT option is what lets a team take Lunatik into a codebase where they want the terms to stay permissive. The repository ships `LICENSE-GPL` and `LICENSE-MIT` at the root, with `LICENSE-MIT` and `LICENSE-GPL` also present, and the Makefile header carries `SPDX-License-Identifier: MIT OR GPL-2.0-only`.

This is not legal advice, and the choice of licence is a question for your own counsel, but the shape of it is worth understanding before you build. A kernel that carries a Lua runtime is a kernel that carries a Lua runtime, and the project has thought about how the pieces are licensed.

The honest limit of the framework is the runtime in the kernel. For the network hot path, eBPF is the tool that the kernel community actually uses, and Lunatik's XDP and TC modules are the place where the two meet. Writing kernel logic in Lua is a large step up in expressiveness, and a large step in what can go wrong, so the projects that make sense here are the ones you could not easily write in C, or the ones where the iteration speed of the REPL outweighs the risk.

## Conclusion

Adopt Lunatik when your team wants to write kernel side logic in a high level language, when the work is the kind that suits event hooks and device drivers, and when you can rebuild a kernel module and carry a Lua runtime in the kernel. Do not adopt it as a replacement for eBPF for the network hot path, since Lua is not the tool for per packet work, and do not plan around it on kernels older than 6.6. Verify first that your kernel has BTF available, since the build asks for `sudo make btf_install`, that the module loads on your distribution, that your licence choice is acceptable, since the project is dual licensed under MIT or GPL-2.0-only, and that the version you take matches the release you read, with v5.1 published on 2026-10-12 and the last push on 2026-10-20.

## FAQ

### What is Lunatik?

Lunatik is a framework for scripting the Linux kernel with Lua. The scripts run inside the kernel and reach kernel facilities through Lua modules, covering character devices, Netfilter, XDP and TC hooks, kprobes, fsnotify, sched_ext, eBPF maps, sockets, netlink, crypto and kernel threads. It supports Linux 6.6 and later.

### How do I build and install Lunatik?

Clone with submodules, then run `sudo make btf_install`, `make` and `sudo make install` from the repository root, which installs the kernel module, the Lua scripts and the `lunatik` command line tool. After that, `sudo lunatik` opens a REPL whose lines run in the kernel.

### How does a Lua script become a kernel device?

The README's example defines a driver table with a name and a mode, implements a read callback, and calls device.new to create the character device. Saved as /lib/modules/lua/passwd.lua, `sudo lunatik run passwd` creates /dev/passwd and `sudo lunatik stop passwd` removes it.

### What licence does Lunatik use?

Lunatik is dual-licensed under MIT or GPL-2.0-only, and the Makefile header carries the SPDX identifier MIT OR GPL-2.0-only. The bundled Lua submodule is MIT, and the Klibc submodule is dual-licensed under BSD 3-Clause or GPL-2.0-only.

## Sources

- [Issues](https://github.com/luainkernel/lunatik/issues)
- [License: GPL-2.0](https://github.com/luainkernel/lunatik/blob/master/LICENSE)
- [luainkernel/lunatik on GitHub](https://github.com/luainkernel/lunatik)
- [README](https://github.com/luainkernel/lunatik/blob/master/README.md)
- [Releases](https://github.com/luainkernel/lunatik/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/luainkernel-lunatik
