HostKit

Elixir-native host management: declare a Linux host, bootstrap packages and runtimes, isolate services with systemd, wire provider integrations, review a plan artifact, then apply it locally or over SSH.

HostKit is for operating real machines without assuming the target already has Elixir, Mix, Docker, or your application runtime installed.

Note

HostKit is currently published as a beta. The core planning/apply workflow is usable and documented, but DSL, provider, and recipe APIs may still change before a stable release.

Why HostKit

Infrastructure code should be boring Elixir, not an opaque pile of shell scripts.

HostKit gives you:

One file: host, runtime, isolated service, reverse proxy

The complete example lives in examples/full_host.exs and is loaded by the test suite so it does not drift.

use HostKit.DSL, providers: [HostKit.Providers.Caddy]
project :prod do
host :app, at: "app.example.com" do
ssh do
user "root"
identity_file Path.expand("~/.ssh/id_ed25519")
accept_hosts true
retry attempts: 3
end
end
bootstrap do
package :ca_certificates
mise do
tool :erlang, "29.0.2"
tool :elixir, "1.20.1"
end
end
service :api do
account system: true
storage :data, mode: 0o750
env :runtime do
secret :database_url, env: "DATABASE_URL"
end
daemon do
env :runtime
exec ["/opt/api/bin/server"]
isolate do
memory_max "512M"
writable :data
network :loopback
end
listen :http, port: 4000
end
caddy_site "api.example.com" do
reverse_proxy :http
end
end
end

This compiles to inspectable HostKit structs and renders ordinary Linux primitives: packages, files, env files, accounts, systemd units, Caddy site config, and systemd hardening directives such as NoNewPrivileges=, ProtectSystem=, RestrictAddressFamilies=, ReadWritePaths=, and memory limits. See the DSL design guidelines for naming, block shape, defaults, and reference style.

Plan, review, apply:

mix host_kit.plan --host app \
--write-package-lock host_kit.package.lock \
--out host_kit.plan.json \
infra/config.exs
mix host_kit.apply --host app \
--plan host_kit.plan.json \
--confirm \
infra/config.exs

secret_env/1 stores an environment-variable reference. Plan artifacts include the variable name, not the resolved secret value.

Managed local demo instance

host is a connection endpoint. instance is a lifecycle-managed compute boundary. Backends such as Incus create/start/destroy the instance, while nested host and service declarations describe how HostKit connects into it and what should run inside.

use HostKit.DSL
project :demo do
instance :demo_vm do
backend :incus, sudo: true
image "images:ubuntu/24.04"
kind :container
lifecycle :ephemeral
expose :ssh, host: 2222, guest: 22
expose :web, host: 18_080, guest: 80
host :guest, at: "127.0.0.1" do
ssh do
user "root"
password "hostkit-demo"
port 2222
accept_hosts true
end
end
service :web do
package :caddy
end
end
end

Manage the declared instance through the backend-neutral instance CLI:

mix host_kit.instance ensure demo_vm infra/demo.exs
mix host_kit.instance status demo_vm infra/demo.exs
mix host_kit.instance destroy demo_vm infra/demo.exs

See examples/livebook_demo_instance.exs for the local Livebook demo target used by the notebook workflow.

Interactive notebook

Deploy real services from Livebook with Kino inputs for SSH target/auth, plan review, explicit apply, and HTTP verification:

Static Caddy site:

Run Caddy notebook in Livebook

Phoenix app from Git, with pinned source revision and source-aware build stamps:

Run Phoenix notebook in Livebook

The notebooks are self-contained and their deployment DSL cells are also exercised by the integration test suite.

Documentation

Development

mix deps.get
mix ci

Run the Incus-backed remote integration on Linux:

HOSTKIT_INCUS_SUDO=true HOSTKIT_SSH_PUBLIC_KEY=$HOME/.ssh/id_ed25519.pub \
scripts/incus_integration_vm.sh ensure
HOSTKIT_INTEGRATION_TOOL=incus HOSTKIT_INCUS_SUDO=true \
mix test test/integration/cli_remote_test.exs --include integration

Status

HostKit is early and intentionally evolving. Runtime APIs come first; Mix tasks wrap them. DSLs compile to plain structs so plans and artifacts remain inspectable.

License

MIT