Pure Elixir tools for systemd unit files and D-Bus manager control
  • Elixir 99.7%
  • Shell 0.3%
Find a file
2026-06-09 19:38:03 +03:00
.github/workflows Add reusable Elixir CI workflow 2026-06-09 00:28:39 +03:00
examples Add idiomatic error and unit-file APIs 2026-06-08 23:58:31 +03:00
guides Add signal waits and sandbox validation 2026-06-09 11:47:32 +03:00
lib Add systemd unit name helpers 2026-06-09 19:36:14 +03:00
scripts Expand validation and release readiness 2026-06-09 00:22:04 +03:00
test Add systemd unit name helpers 2026-06-09 19:36:14 +03:00
.credo.exs Create systemd package scaffold 2026-06-08 22:21:03 +03:00
.formatter.exs Create systemd package scaffold 2026-06-08 22:21:03 +03:00
.gitignore Create systemd package scaffold 2026-06-08 22:21:03 +03:00
.reach.exs Create systemd package scaffold 2026-06-08 22:21:03 +03:00
AGENTS.md Use parser-based unit value validation 2026-06-09 00:24:21 +03:00
CHANGELOG.md Bump systemdkit to 0.1.2 2026-06-09 19:38:03 +03:00
CONTRIBUTING.md Prepare systemdkit 0.1.0 release 2026-06-09 12:02:10 +03:00
LICENSE Fix license copyright name 2026-06-09 01:22:21 +03:00
mix.exs Bump systemdkit to 0.1.2 2026-06-09 19:38:03 +03:00
mix.lock Add high-level APIs and docs 2026-06-08 23:41:13 +03:00
README.md Rename package app to systemdkit 2026-06-09 12:18:32 +03:00

systemdkit

Pure Elixir tools for systemd unit files and D-Bus manager control.

systemdkit is for Elixir applications and deployment tools that need to generate unit files, inspect systemd state, or control systemd directly over D-Bus without shelling out to systemctl.

Installation

The Hex package and Mix application are named systemdkit; public modules use the Systemd namespace.

def deps do
  [
    {:systemdkit, "~> 0.1.1"}
  ]
end

Unit files

Build systemd units with typed helpers, render them, and validate them before installation:

unit_file =
  Systemd.UnitFile.service(
    unit: [description: "My app", after: "network.target"],
    service: [
      type: :exec,
      user: "deploy",
      working_directory: "/opt/my-app/current",
      exec_start: "/opt/my-app/current/bin/my_app start",
      restart: "on-failure",
      memory_max: "512M",
      tasks_max: 512,
      no_new_privileges: true,
      protect_system: :strict
    ],
    install: [wanted_by: "multi-user.target"]
  )

:ok = Systemd.UnitFile.validate(unit_file, :service)
Systemd.UnitFile.to_string(unit_file)

Parsing is loss-aware: comments, blank lines, duplicate directives, reset directives, and source spans are preserved.

{:ok, unit_file} = Systemd.UnitFile.parse("[Service]\nExecStart=/bin/true\n")
Systemd.UnitFile.get_all(unit_file, "Service", "ExecStart")

Builders are available for service, socket, timer, mount, path, and target units.

D-Bus manager control

Use the top-level API for short-lived D-Bus operations:

{:ok, units} = Systemd.list_units()
{:ok, unit_files} = Systemd.list_unit_files()
{:ok, state} = Systemd.unit_state("dbus.service")

:ok = Systemd.reload()
:ok = Systemd.start_unit("my_app.service")
:ok = Systemd.restart_unit("my_app.service")

Use Systemd.Manager when you want to reuse a connection or inspect jobs:

Systemd.with_connection([], fn conn ->
  with {:ok, job} <- Systemd.Manager.restart_unit(conn, "my_app.service"),
       :ok <- Systemd.Job.await_signal(conn, job, timeout: 10_000) do
    :ok
  end
end)

Errors and permissions

APIs return {:ok, value} or {:error, %Systemd.Error{}}. Permission and polkit failures are classified as :permission:

case Systemd.start_unit("my_app.service") do
  :ok -> :ok
  {:error, error} ->
    if Systemd.Error.permission?(error), do: {:error, :permission_denied}, else: {:error, error}
end

Read-only calls often work unprivileged. Mutating system units typically require root or appropriate polkit rules. systemdkit reports those D-Bus errors directly; it does not retry through sudo.

For user units, pass bus: :session when a systemd user session bus is available:

Systemd.list_units(bus: :session)

Guides

Integration testing

The test suite includes optional integration tests against a real Linux systemd manager. They are excluded by default:

mix test

Run them on Linux with systemd and a system bus:

SYSTEMD_INTEGRATION=1 mix test

This repository also includes a Lima helper used by maintainers:

scripts/integration_test.sh