Skip to content

bluez: new sysext shipping the BlueZ Bluetooth userspace - #253

Open
ananthb wants to merge 2 commits into
flatcar:mainfrom
ananthb:bluez-sysext
Open

bluez: new sysext shipping the BlueZ Bluetooth userspace#253
ananthb wants to merge 2 commits into
flatcar:mainfrom
ananthb:bluez-sysext

Conversation

@ananthb

@ananthb ananthb commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Adds a bluez sysext shipping the BlueZ Bluetooth userspace: bluetoothd plus bluetoothctl, btmon, btmgmt, btattach and the legacy tools that are still packaged.

This is the userspace half of adding Bluetooth support to Flatcar. The kernel half is flatcar/scripts#4197, which enables the Bluetooth modules in the image.

How it's built

BlueZ has no upstream static or portable release and links glib, dbus and readline, so the binaries are bundled with their full library closure (glibc and the dynamic loader included) from a Debian container and handed to tools/flix.sh, which patchelf's them onto a private loader/rpath. Same approach as qemu.sysext and tilde.sysext.

The Debian layout is probed rather than hardcoded: bluetoothd moved from /usr/lib to /usr/libexec across releases, and the deprecated tools (hciconfig, hcitool, …) are absent from newer ones.

Behaviour on merge

bluetooth.service is upheld by multi-user.target and runs bluetoothd as a D-Bus service under org.bluez.

The unit is guarded by ConditionPathIsDirectory=/sys/class/bluetooth. On an image without the Bluetooth modules the unit is skipped rather than failed. On an image that has them, the controller's driver autoloads on detection, the directory appears, and the daemon starts.

/var/lib/bluetooth (adapter settings and pairing keys) is created by a shipped tmpfiles snippet.

Testing

Built for both architectures against Debian stable (BlueZ 5.82).

For each, I extracted the image and ran the bundled binaries on an Alpine (musl, non-Debian) host with only /usr/local/bluez bind-mounted — bluetoothd, bluetoothctl and btmon all start and report 5.82, which confirms the library closure is genuinely self-contained and not falling back to host libraries.

I also checked that the image contains no /usr/sbin (per the warning in _skel.sysext/create.sh) and that the D-Bus policy, units and tmpfiles snippet land where they should.

Adds a bluez extension providing bluetoothd plus bluetoothctl, btmon,
btmgmt and btattach, so Bluetooth can be used on Flatcar without baking
BlueZ into the OS image.

BlueZ has no upstream static or portable release and links glib, dbus and
readline, so the binaries are bundled with their library closure (glibc
and the dynamic loader included) from a Debian container and handed to
tools/flix.sh, which patchelf's them onto a private loader/rpath. This is
the same approach as qemu.sysext and tilde.sysext. Debian packages BlueZ,
so the version parameter selects the suite (stable/testing) shipping it.

The extension is userspace only. Bluetooth kernel modules must match the
running kernel exactly, so they belong in the image; flatcar/scripts#4197
enables them. bluetooth.service is guarded by ConditionPathIsDirectory on
/sys/class/bluetooth, so on an image without those modules the unit is
skipped rather than failed, and the Upholds= drop-in does not retry it in
a loop.

The unit reloads D-Bus configuration before starting bluetoothd: the
extension drops a new policy file into /usr/share/dbus-1/system.d, which a
running dbus-daemon has not read yet, and without the reload bluetoothd
cannot take the org.bluez name until the next reboot.

Built and checked for both architectures against Debian stable (BlueZ
5.82): the bundled bluetoothd, bluetoothctl and btmon all run and report
their version on an Alpine (musl, non-Debian) host, confirming the closure
is self-contained.

Signed-off-by: Ananth Bhaskararaman <antsub@gmail.com>
Copilot AI lite review requested due to automatic review settings August 9, 2026 09:17
@ananthb
ananthb requested a review from a team as a code owner August 9, 2026 09:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new bluez system extension to ship the BlueZ Bluetooth userspace (daemon + CLI tools) on Flatcar via the sysext-bakery, along with the needed systemd integration and documentation/version metadata.

Changes:

  • Introduces bluez.sysext/create.sh to build BlueZ from Debian (stable/testing) and bundle the full shared-library closure.
  • Adds system integration files (systemd unit + start-on-merge drop-in, tmpfiles entry) so bluetoothd can run and persist state.
  • Registers the extension in version metadata and documentation, including a dedicated docs/bluez.md page.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
release_build_versions.txt Registers bluez versions (5.82, latest) for the bakery build matrix.
docs/index.md Adds bluez to the published extensions index.
docs/bluez.md New user documentation and Butane provisioning snippet for the BlueZ sysext.
bluez.sysext/test.sh Adds the extension test entry point (currently empty).
bluez.sysext/files/usr/lib/tmpfiles.d/10-bluez.conf Creates /var/lib/bluetooth for adapter state and pairing keys.
bluez.sysext/files/usr/lib/systemd/system/multi-user.target.d/10-bluez.conf Ensures bluetooth.service is upheld by multi-user.target (start-on-merge behavior).
bluez.sysext/files/usr/lib/systemd/system/bluetooth.service Defines the bluetoothd systemd unit (D-Bus service, guarded by sysfs presence).
bluez.sysext/create.sh Implements the Debian-based build, suite/version selection, and closure bundling via tools/flix.sh.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/bluez.md
Comment thread bluez.sysext/files/usr/lib/systemd/system/bluetooth.service Outdated
Address review feedback:

- bluetooth.service used WantedBy=bluetooth.target, following upstream
  BlueZ. systemd does ship that target, but nothing pulls it in by
  default, so 'systemctl enable bluetooth.service' would leave the
  service unstarted. Every other service unit in this repo uses
  multi-user.target; match that.

- The docs pointed the sysupdate config at .../extensions/bluez/bluez.conf.
  That nested form does resolve (chrony and bird use it), but the flat
  .../extensions/<name>.conf form is what the other 28 docs use.

Signed-off-by: Ananth Bhaskararaman <antsub@gmail.com>
Copilot AI review requested due to automatic review settings August 9, 2026 09:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (1)

bluez.sysext/files/usr/lib/systemd/system/bluetooth.service:21

  • LimitNPROC=1 is very likely too restrictive for bluetoothd (threads count toward RLIMIT_NPROC on Linux), and it also deviates from the repo’s established convention of setting this to infinity for long-running daemons (e.g. docker.sysext/files/usr/lib/systemd/system/docker.service:15, containerd.sysext/.../containerd.service:11). This could cause startup/runtime failures under GLib/D-Bus as soon as an additional thread/task is created.
LimitNPROC=1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants