We're hiring!
*

From Lua to JSON: refactoring WirePlumber's configuration system

Ashok Sidipotu avatar

Ashok Sidipotu
October 27, 2022

Share this post:

Reading time:

Refactoring WirePlumber's configuration system is the first big feature I took up since I joined the PipeWire/WirePlumber team a year back. It's a year well spent in my professional life, hanging around with caring people and truly open source technology. With what I have seen in the multimedia stacks, I honestly believe PipeWire is the next generation multimedia server and WirePlumber playing the role of enhancing its utility and appeal.

Let me cut back to the subject at hand.

WirePlumber's old Lua configuration

As you might already know, WirePlumber is a heavily modular session/policy manager for PipeWire and it uses the Lua language both for scripting high-level logic and for defining its configuration. To avoid misunderstandings, I will only be debating the use of Lua as a configuration language.

Using Lua as a configuration language has some advantages as it integrates easily and readily with both Lua code and C code. As a plus, the implementation of rule/condition based settings (this is a hallmark of PipeWire that every entity in it is an object, every object has properties, and these properties can be used to apply settings) is a breeze in Lua.

However, there are some gaping disadvantages. To name a few: the settings cannot be changed at runtime as they're static settings, user overrides are possible but they are neither elegant nor intuitive, and validating the configuration with a schema is next to impossible.

For what it's worth, I have quite enjoyed using Lua as both the scripting language and the configuration system. However, it's time to say goodbye to using it as the configuration system.

A new JSON configuration system

I was all buoyed up when I came across the possibility of refactoring the whole WirePlumber configuration system. Honestly, who gets an opportunity to build things from the ground up like this these days? I don't know about you, but I haven't had many during my professional career.

After careful thinking and consideration, we have decided to use PipeWire's JSON syntax to define settings. This overcomes the disadvantages of the Lua configuration and also gives a more unified configuration approach across the whole PipeWire ecosystem.

PipeWire's JSON syntax is a variant of JSON called "SPA JSON" that is built into PipeWire. The SPA JSON parser is a very lightweight parser that mostly ignores all intermediate characters and therefore can parse a wide range of variants, including strict JSON. For instance, the following examples are all valid configuration files:

# Usual pipewire configuration variant
wireplumber.components = [
  { name = libwireplumber-module-default-nodes , type = module },
  { name = policy-device-profile.lua, type = script/lua }
]

# Actual JSON
"wireplumber.components" : [
  { "name" : "libwireplumber-module-default-nodes" , "type" : "module" }
  { "name" : "policy-device-profile.lua", "type" : "script/lua" }
]

# Even more loose syntax without separator characters
wireplumber.components  [
  { name  libwireplumber-module-default-nodes   type  module }
  { name  policy-device-profile.lua   type  script/lua }
]

Now back to new JSON configuration system. Settings are now defined under a new section, "wireplumber.settings", in the main configuration file (wireplumber.conf). This section is not defined as monolith but instead is distributed into different setting files (*.conf) under wireplumber.conf.d/. WirePlumber will glean through these files and stitch them up during startup.

Each conf file is a logical grouping of settings, modules, and scripts.

For example: below is the device.conf, which contains all the device-related configuration.

# Settings to Track/store/restore user choices about devices

wireplumber.settings = {
  # Below syntax defines key-value pair style settings.
  device.use-persistent-storage = true
  device.auto-echo-cancel = true
  device.echo-cancel-sink-name = echo-cancel-sink
  device.echo-cancel-source-name = echo-cancel-source

  # Below syntax defines a rule/condition based settings.
  device.rules = [
    {
      matches = [
          # Matches all devices
          { device.name = "~*" }
      ]
      actions = {
        update-props = {
          profile_names = "off pro-audio"
        }
      }
    }
  ]
}

# WirePlumber modules and scripts are also loaded from the config files.
wireplumber.components = [
  { name = libwireplumber-module-default-nodes , type = module }
  { name = policy-device-profile.lua, type = script/lua }
]

Just to make it easier for the users who are familiar with the Lua config, I drew up this table mapping the old Lua config files and their corresponding new JSON config files:

Old Lua config file New JSON config file
10-default-policy.lua policy.conf
40-device-defaults.lua, 50-default-access-config.lua device.conf
40-stream-defaults.lua stream.conf
20-default-access.lua access.conf
30-alsa-monitor.lua, 50-alsa-config.lua alsa.conf
30-libcamera-monitor.lua, 50-libcamera-config.lua libcamera.conf
30-v4l2-monitor.lua, 50-v4l2-config.lua v4l2.conf


As you might have noticed, in some cases, two Lua config files (in bold above) are merged into a single JSON config file. We hope this will facilitate much better modularization of functionality.

Now let's take a look at the system features & design, and client functions of this new JSON configuration system.

JSON configuration system features

Dynamic settings

During startup, WirePlumber loads all the settings from .conf files into a PipeWire metadata object called "sm-settings". Lua scripts, modules, and WirePlumber clients can use PipeWire metadata tools and API to change the settings at runtime. As you may know, one can issue these commands from the command prompt as well.

For example:

pw-metadata -n sm-settings 0 "policy.default.move" true Spa:String:JSON
pw-metadata -n sm-settings 0 "device.echo-cancel-source-name" "echo-cancel-source-bal" Spa:String:JSON

The above commands do not just change settings at runtime, but the changes are also applied live on WirePlumber, as explained in the below section.

Callbacks from settings

Lua scripts, modules, or WirePlumber clients interested in any of the settings can also subscribe for callbacks to know the changes in settings. This enables them to not only know the changes in settings, but also to apply the changes live.

Let me give an example to drive home the point here. You must be aware that WirePlumber saves the stream properties (volume, mute status etc). Now you can turn off this behavior runtime with the below command, no need for restart/reboot. Cool, isn't it?

pw-metadata -n sm-settings 0 stream.restore-props false Spa:String:JSON

I felt thrilled in enabling this feature across all the scripts and modules, as users can now experiment with different settings at runtime.

Please be informed that some of the changes to settings may not take effect or cause some undesired behavior. Not every setting is tested in this perspective. We may need your help here.

Easy user overrides

Easy user overrides is by far the most handy outcome of this whole exercise.

JSON facilitates for much better user/custom overrides on top of the default settings.

Sound too formal? Allow me to put things into perspective. Let's say a user wanted to customize the stream settings of WirePlumber. They would have to copy the stream config file (/usr/share/wireplumber/40-stream-defaults.lua), change the part they need to, place it in /etc/wireplumber/40-stream-defaults.lua, and restart WirePlumber. WirePlumber always loads this new configuration file and ignores the default configuration file. Now, what if this file changes upstream? In this case, the user will likely land into trouble the moment WirePlumber is upgraded.

Today the overrides work at the configuration file level. Easy overrides extend this all the way to the level of the individual setting. So this means users can only touch the settings they are interested in. We hope this will make the job of distribution packagers easier as well.

PipeWire integration

WirePlumber settings will follow the same syntax as the rest of the PipeWire and WirePlumber configs. In other words, WirePlumber settings are like any other PipeWire configuration.

Persistency

If a User/Client wants to change the settings at runtime (using pw-metadata as explained in Dynamic settings) then we recommend considering enabling persistent behavior (or simply persistency), so that the setting changes are saved to state file and are remembered across reboots.

When Persistency is enabled, the settings will be read from the config files only once and for subsequent reboots, they will be initialized from the state file. Please note that Persistency is disabled by default. It can be enabled with the below setting in wireplumber.conf

wireplumber.settings = {
  persistent.settings = true
}

Simple, powerful, and effective!

Client access

Clients that are built with the WirePlumber library will now be able to transparently access the runtime settings that the WirePlumber daemon is currently running with.

To throw another possibility at you, users can now add new settings in .conf or through pw-metadata and start querying them from their scripts/modules and build logic around it. Building this sort of developer-friendly stuff is what keeps us going.

Schema validation

JSON settings allow us to do validation against a schema. This feature has been taken into consideration, but it will not be included in the first release of this new system, as more work is required to complete it.

JSON configuration system design

As you can see, compared to Lua, we had to build quite a bit of infrastructure. Personally, I have been on this for the last 2-3 months. We believe it's all worth it in terms of the rich functionality that is described above.

JSON configuration system client functions

WirePlumber clients can access settings using two methods:

  • WP Settings API

    WpSettings loads and parses the “sm-settings” metadata, which contains WirePlumber settings and rules. It provides new APIs to its clients (modules, lua scripts, etc) to access, change, and follow them.

    Below is a quick outline of APIs.

    • wp_settings_get() API to access the values of settings.
    • wp_settings_apply_rule() to apply the rule-based settings.
    • wp_settings_subscribe() to subscribe for callbacks on settings.
  • "sm-settings" Metadata interface

    Clients can also interact with settings via the familiar PipeWire metadata tools and APIs.

Either of these APIs can be used to build a GUI front-end to modify WirePlumber settings.

Status & availability

Almost all the needed changes are landed in next-rebased branch. The branch is in reasonably good shape, myself and few of my colleagues have installed and are using it without any issues.

If you have come this far, I kindly ask you to extend the favor by trying this branch out. Please give it a try and let us know if you like it, have suggestions, or face any bugs.

A note on the WirePlumber 0.5 release

Soon, WirePlumber will be upgrading from 0.4.x to 0.5. This will be a major upgrade with significant churn. We are making some fundamental changes to the WirePlumber system, with the configs revamp being one of them. We are aiming to roll out 0.5 sometime before the end of this year.

We are aiming to do a few more blog posts on this release, so if you are equally enthusiastic about learning more, stay tuned!

Continue reading: WirePlumber's Event Dispatcher: a new, simplified way of handling PipeWire events.

Search the newsroom

Latest Blog Posts

Re-thinking framebuffers in PanVK

23/03/2026

PanVK’s new framebuffer abstraction for Mali GPUs removes OpenGL-specific constraints, unlocking more flexible tiled rendering features…

Running Mainline Linux, U-Boot, and Mesa on Rockchip: A year in review

02/03/2026

Get the recap of Nicolas Frattaroli's FOSDEM talk detailing Rockchip’s mainline progress, including Vulkan 1.4 and NPU support as a vital…

Now streaming: Collabora XDC 2025 presentations

02/12/2025

As an active member of the freedesktop community, Collabora was busy at XDC 2025. Our graphics team delivered five talks, helped out in…

Implementing Bluetooth LE Audio & Auracast on Linux systems

24/11/2025

LE Audio introduces a modern, low-power, low-latency Bluetooth® audio architecture that overcomes the limitations of classic Bluetooth®…

Strengthening KernelCI: New architecture, storage, and integrations

17/11/2025

Collabora’s long-term leadership in KernelCI has delivered a completely revamped architecture, new tooling, stronger infrastructure, and…

Font recognition reimagined with FasterViT-2

11/11/2025

Collabora extended the AdobeVFR dataset and trained a FasterViT-2 font recognition model on millions of samples. The result is a state-of-the-art…

Open Since 2005 logo

Our website only uses a strictly necessary session cookie provided by our CMS system. To find out more please follow this link.

Collabora Limited © 2005-2026. All rights reserved. Privacy Notice. Sitemap.