Skip to main content

README.md

# unitctl

Docker-like process controls backed by pure systemd primitives from Elixir.

`unitctl` is the opinionated runtime layer above [`systemdkit`](https://hex.pm/packages/systemdkit). It starts and controls transient systemd services over D-Bus, without Docker and without shelling out to `systemctl`.

This package is intentionally early. It exists to claim the shape of the API and keep the low-level `systemdkit` package focused.

## Installation

```elixir
def deps do
  [
    {:unitctl, "~> 0.1.0-pre"}
  ]
end
```

## Example

```elixir
{:ok, instance} =
  Unitctl.start(
    name: "demo-worker",
    command: ["/usr/bin/env", "sleep", "60"],
    description: "Demo worker",
    environment: %{"MIX_ENV" => "prod"},
    working_directory: "/srv/demo",
    resources: %{
      memory_max: 256 * 1024 * 1024,
      tasks_max: 64,
      cpu_quota: 500_000
    },
    sandbox: %{
      no_new_privileges: true,
      private_tmp: true,
      protect_system: "strict"
    }
  )

{:ok, state} = Unitctl.inspect(instance)
{:ok, stats} = Unitctl.stats(instance)
:ok = Unitctl.stop(instance)
```

## Scope

`systemdkit` answers: “How do I talk to systemd correctly from Elixir?”

`unitctl` answers: “How do I run and control app processes with container-like ergonomics using systemd?”

Planned areas:

- transient services and scopes
- resource controls through cgroups
- sandbox/security presets
- filesystem isolation
- credentials/secrets
- journald logs/follow
- stats and inspect APIs
- deployment-runtime primitives for Xamal-style bare-metal releases

## Integration tests

Integration tests require Linux with systemd and a system bus:

```sh
SYSTEMD_INTEGRATION=1 mix test
```

Maintainers can run the Lima-backed test wrapper from macOS:

```sh
scripts/integration_test.sh
```

## Non-goals

- no Docker dependency
- no `systemctl` shell wrappers
- no OCI image lifecycle in the first layer