Plugins and Extensions

Extend luet with plugins and extensions

Luet can be extended in 2 ways by extensions and plugins.

Before you begin

You need to have a working luet binary installed.


Extensions expand Luet featureset horizontally, so for example, “luet geniso” will allow you to build an iso using luet, without this needing to be part of the luet core.

An Extension is nothing more than a standalone executable file, whose name begins with luet-. To install an extension, simply move its executable file to anywhere on your system PATH.

All the plugins will be accessible to luet as luet pluginname

Writing an Extension

You can write an extension in any programming language or script that allows you to write command-line commands.

Executables receive the inherited environment from luet. An extension determines which command path it wishes to implement based on its name. For example, a plugin wanting to provide a new command luet foo, would simply be named luet-foo, and live somewhere in your PATH.

Example Extension


if [[ "$1" == "help" ]]
    echo "Extension help"
    exit 0

if [[ "$1" == "run" ]]
    # do something interesting

echo "I am an Extension named luet-foo"

Using an Extension

To use the above extension, simply make it executable:

$ sudo chmod +x ./luet-foo

and place it anywhere in your PATH:

$ sudo mv ./luet-foo /usr/local/bin

You may now invoke your extension as a luet command:

$ luet foo
I am an Extension named luet-foo

All args and flags are passed as-is to the executable:

$ luet foo help

Extension help


Plugins instead are expanding Luet vertically by hooking into internal events. Plugins and Extensions can be written in any language, bash included! Luet uses go-pluggable so it can dispatch events to external binaries.

Similarly to Extensions, a Plugin is nothing more than a standalone executable file, but without any special prefix. To install a plugin, simply move its executable file to anywhere on your system PATH.

Differently from Extensions, they are not available from the CLI and cannot be invoked directly by the user, instead they are called by Luet during its lifecycle.

Writing a Plugin

You can write a plugin in any programming language or script.

The first argument that is passed to a plugin will always be the event that was emitted by Luet in its lifecycle. You can see all the events available here. The second argument, is a JSON encoded payload of the object that Luet is emitting with the event. The object(s) may vary depending on the emitted event.

The output of the plugin (stdout) will be parsed as JSON. Every plugin must return a valid JSON at the end of its execution, or it will be marked as failed and stops luet further execution. See also the go-pluggable README.

The returning payload should be in the following form:

{ "state": "", "data": "data", "error": ""}

By returning a json with the error field not empty, it will make fail the overall execution.

Example Plugin

echo "$1" >> /tmp/event.txt
echo "$2" >> /tmp/payload.txt

echo "{}"

Using a plugin

To use the above plugin, simply make it executable:

$ sudo chmod +x ./test-foo

and place it anywhere in your PATH:

$ sudo mv ./test-foo /usr/local/bin

Now, when running luet, add --plugin test-foo:

$ luet --plugin test-foo install -y foopackage

And check /tmp/event.txt to see the event fired and /tmp/payload.txt to check the payloads that were emitted by Luet.

Concrete example

A plugin that prints the images that are being built in /tmp/exec.log:

exec >> /tmp/exec.log
exec 2>&1
if [ "$event" == "" ]; then
  image=$(echo "$payload" | jq -r .data | jq -r .ImageName )
    echo "{ \"data\": \"$image built\" }"
    echo "{}"

Last modified June 24, 2024: Update goreleaser command (1712988)