Using Scribunto Extension for Lua Scripting in MediaWiki

Why Scribunto matters (and why you might be curious right now)

Why Scribunto matters (and why you might be curious right now)

Okay, picture this: you’re editing a wiki page and you suddenly wish you could run a tiny script right there, without having to spin up a whole separate server or embed a massive JavaScript blob. Enter Scribunto, the MediaWiki extension that lets you write Lua modules and call them from wikitext with {{#invoke:…}}. It feels a bit like adding a Swiss‑army knife to your toolbox – you get a programmable layer that’s still safe, sandboxed, and, best of all, reusable across pages.

Since MediaWiki 1.34 the extension ships with the core, but you’ll still have to tweak a few settings to make it sing. Below you’ll find a loosely‑structured walk‑through that jumps from “hey, this is how you install it” to “here’s a quirky gotcha that tripped me up last week”. No‑fluff, a few sidebars, and yes – a dash of personality.

Getting Scribunto onto your wiki (the “install‑it‑now” part)

  • Step 3 – verify. Visit Special:Version and look for “Scribunto” in the extensions list. If it shows up, you’re good to go.

Step 2 – tell MediaWiki to load the extension. Drop these lines at the very bottom of LocalSettings.php (yes, the bottom, because some other extensions may want to override defaults earlier):

wfLoadExtension( 'Scribunto' );
$wgScribuntoDefaultEngine = 'luastandalone'; // the bundled binary works for most setups

Step 1 – grab the code. If you’re on a recent MediaWiki release you probably already have the Scribunto folder under extensions/. Otherwise, clone it straight from Wikimedia’s repo:

cd extensions/
git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/Scribunto

If you prefer a tarball, the same URL works with a .tar.gz suffix.

Do you need the Lua binary?

Most installations are happy with the bundled binaries (they cover Linux x86‑64, macOS, and Windows). But if you’re on an exotic platform – say a Raspberry Pi or an old 32‑bit server – you’ll have to point Scribunto at a custom interpreter. Add this to LocalSettings.php (replace /usr/bin/lua5.1 with wherever your binary lives):

$wgScribuntoEngineConf['luastandalone']['luaPath'] = '/usr/bin/lua5.1';

Basic concepts you should know before you start scripting

  • Modules live in their own namespace. By default it’s Module: (ID 828). When you create a new Lua file, you do it as Module:MyCoolThing.
  • Sandboxed environment. Each call runs in its own sandbox – variables don’t leak between calls, which keeps things tidy but also means you can’t share state without explicit storage (e.g. mw.store).

The #invoke parser function. This is how you call a function inside a module from normal wikitext. Syntax is:

{{#invoke:Module_name|function_name|arg1|arg2|…}}

Quick “Hello, world!” example

Let’s get our hands dirty. Create a page Module:Hello with this content:

local p = {}

function p.greet(frame)
    local name = frame.args[1] or "world"
    return "Hello, " .. name .. "!"
end

return p

Now drop this onto any wiki page:

{{#invoke:Hello|greet|Alice}}

and you’ll see “Hello, Alice!”. If you leave out the argument you get “Hello, world!”. Easy, right?

Going a step further – real‑world patterns

Most wikis use Scribunto for slightly more sophisticated tasks: infoboxes, navigation templates, or even on‑the‑fly calculations. Below are three common patterns, each with a short code snippet.

1. Infoboxes that pull data from page properties

Suppose you have a page that stores a population property. Your module can read that and format a table:

local p = {}

function p.infobox(frame)
    local title = mw.title.getCurrentTitle()
    local pop = title.properties.population or "unknown"
    return string.format(
        "'''%s'''Population: %s", title.text, pop
    )
end

return p

Usage on a page:

{{#invoke:MyInfobox|infobox}}

Generating a list of sibling pages can be a pain in pure wikitext. Lua makes it painless:

local p = {}

function p.nav(frame)
    local cat = frame.args[1] or "Category:Examples"
    local pages = mw.title.makeTitle( nil, cat ):getCategoryMembers()
    local out = {}
    for _, page in ipairs(pages) do
        table.insert(out, "[[" .. page.title .. "]]")
    end
    return table.concat(out, " • ")
end

return p

And on a page you just write:

{{#invoke:NavHelper|nav|Category:Animals}}

3. Simple math without leaving the page

Need to calculate a percentage? No problem:

local p = {}

function p.percent(frame)
    local a = tonumber(frame.args[1]) or 0
    local b = tonumber(frame.args[2]) or 1
    if b == 0 then return "∞" end
    return string.format("%.2f%%", (a/b)*100)
end

return p

Use it like:

{{#invoke:Calc|percent|42|128}}

Optional goodies – making the editing experience nicer

If you want a richer editor for your Lua modules (syntax highlighting, auto‑indent, etc.), consider adding one or more of the following extensions. They’re not required for Scribunto to work, but they feel like a fresh coat of paint over a solid foundation.

  • WikiEditor – basic WYSIWYG editing, works out‑of‑the‑box.
  • SyntaxHighlight – adds <syntaxhighlight lang="lua"> tags, handy for documentation.

CodeEditor – a full‑blown code editor with CodeMirror under the hood. Enable it for modules by setting:

$wgScribuntoUseCodeEditor = true;

Configuration deep‑dive (the stuff that makes Scribunto tick)

Most admins never touch these, but if you need to tighten resources or change the engine, here’s the cheat‑sheet.

VariableTypical valueWhat it does
$wgScribuntoDefaultEngine'luastandalone' (or 'luasandbox')Chooses which engine runs your modules. luasandbox is a PHP‑based engine that can be faster but is not bundled by default.
$wgScribuntoEngineConf['luastandalone']['luaPath']null (uses bundled binary)Path to a custom Lua interpreter if you need one.
$wgScribuntoEngineConf['luastandalone']['memoryLimit']52428800 (≈ 50 MB)Maximum memory a module may allocate. Adjust if you see “memory exhausted” errors.
$wgScribuntoEngineConf['luastandalone']['cpuLimit']7 secondsCPU time limit per invocation. Raise carefully – you don’t want rogue scripts hogging the server.
$wgScribuntoEngineConf['luastandalone']['errorFile']nullWhere the interpreter writes stderr. Set to a writable log file to capture cryptic errors.

Debugging – the “what‑the‑heck‑happened?” section

When a module throws an error you’ll see a big red “Lua error: …” message on the page. Here’s how to make sense of it.

  1. Enable the debug console. While editing a module, scroll down below the edit box – there’s a tiny “Debug console” textbox. Paste the problematic call (e.g. {{#invoke:MyModule|myFunc}}) and hit “Run”. The console will show the raw Lua error plus a stack trace.
  2. Check the error log. If you set $wgScribuntoEngineConf['luastandalone']['errorFile'], open that file on the server. You’ll see the exact stdout/stderr from the Lua binary – invaluable for “internal error: interpreter exited with status 1”.
  3. Common pitfalls.
    • Missing proc_open – if your PHP disable_functions list includes proc_open, Scribunto can’t spawn the Lua process. Remove it from php.ini or ask your host.
    • Binary permissions. On Linux, the bundled lua must be executable. Run chmod a+x extensions/Scribunto/includes/Engines/LuaStandalone/binaries/*/lua.
    • SELinux “noexec”. Some shared hosts mount /var/www with noexec. Either move the binaries to an exec‑allowed location or disable the enforcement (if you control the server).

Troubleshooting

Below is a short checklist you can copy‑paste into a wiki page for quick reference (feel free to adapt).

  • “Script error” link is clickable – it opens a detailed stack trace.
  • If you see “version ‘GLIBC_2.11’ not found”, you’re on an old Linux distro. Either upgrade the C library or download a custom Lua binary compiled against your system’s glibc.
  • On ARM (Raspberry Pi) the default binary is for x86. Point luaPath at /usr/bin/lua5.1 (or build your own). The error message will mention “Intel 80386” if you’re using the wrong binary.
  • Memory‑limit errors appear as “Lua error: Internal error: The interpreter exited with status 2”. Bump memoryLimit in LocalSettings.php.
  • “Internal error: status 126” usually means the binary isn’t executable or the filesystem blocks execution. Fix permissions or move the binary.
  • Blank screen after enabling Scribunto? You’re probably on an outdated MediaWiki version that doesn’t match the extension’s release branch. Upgrade MediaWiki or roll back to a matching Scribunto tag.

Best practices – how to keep your Lua modules tidy and performant

  1. Keep functions small. A module with one monolithic function is harder to test and more likely to hit the CPU time limit.
  2. Cache expensive results. Use mw.loadData or mw.smw.getQuery (if you have Semantic MediaWiki) and store results in mw.store for subsequent calls.
  3. Document your API. Add a --- comment block at the top of each module describing argument order; it helps future editors and tools like mw.toolforge.org can auto‑generate API docs.
  4. Avoid global side‑effects. Since each #invoke gets a fresh sandbox, you might be tempted to use globals for “shared state”. That won’t work – use the storage APIs instead.
  5. Unit‑test with the debug console. The console can be used as a REPL; write small test calls before you commit a big change.

Wrapping up

If you’ve made it this far, congratulations – you’ve gone from “I heard about Scribunto” to “I’m writing real Lua modules on my wiki”. The next steps are up to you: maybe start a community library of reusable modules, or contribute back to the upstream repo if you discover a bug or a clever pattern.

Remember, Scribunto isn’t a silver bullet; it shines when you need deterministic, sandboxed logic that lives close to the content. When you combine it with other extensions (like Capiunto for infoboxes, or Semantic Scribunto for data‑driven queries) you get a truly dynamic wiki without compromising security

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe