HostKit's remote flow is designed for machines that may not have Elixir, Mix, or application runtimes installed yet.
The control machine runs HostKit. The target machine only needs SSH and a supported package manager.
Declare the host
use HostKit.DSL
project :prod do
host :server do
hostname "203.0.113.10"
user "root"
sudo true
ssh password: secret_env("HOSTKIT_SSH_PASSWORD"),
silently_accept_hosts: true
end
service :bootstrap do
package :ca_certificates
package :curl
mise path: "/usr/local/bin/mise", system_data_dir: "/usr/local/share/mise" do
tool :erlang, "29.0.2"
tool :elixir, "1.20.1"
end
end
endIdentity-file auth is preferred when available:
ssh identity_file: Path.expand("~/.ssh/id_ed25519"),
silently_accept_hosts: truePackage resolution and locks
HostKit resolves semantic package names to target package names using Repology. Write a package lock when planning for repeatable applies:
mix host_kit.plan --host server \
--write-package-lock host_kit.package.lock \
infra/config.exs
Apply with the lock:
mix host_kit.apply --host server \
--package-lock host_kit.package.lock \
--confirm \
infra/config.exs
Plan artifacts
For production, prefer a two-step artifact flow:
HOSTKIT_SSH_PASSWORD='...' mix host_kit.plan --host server \
--package-lock host_kit.package.lock \
--out host_kit.plan.json \
infra/config.exs
Review host_kit.plan.json, then apply the reviewed artifact:
HOSTKIT_SSH_PASSWORD='...' mix host_kit.apply --host server \
--plan host_kit.plan.json \
--confirm \
infra/config.exs
Artifacts include target metadata such as package manager/repository. apply --plan validates that metadata before applying package changes.
Secret safety
secret_env/1 serializes as a reference:
{
"$type": "struct",
"module": "Elixir.HostKit.Secret",
"fields": {
"source": {
"$type": "tuple",
"items": [
{"$type": "atom", "value": "env"},
"HOSTKIT_SSH_PASSWORD"
]
}
}
}The resolved secret value is not stored in the artifact.
Linux integration with Incus
Create an Incus-backed target:
HOSTKIT_INCUS_SUDO=true HOSTKIT_SSH_PUBLIC_KEY=$HOME/.ssh/id_ed25519.pub \
scripts/incus_integration_vm.sh ensure
Run the remote CLI integration against it:
HOSTKIT_INTEGRATION_TOOL=incus HOSTKIT_INCUS_SUDO=true \
mix test test/integration/cli_remote_test.exs --include integration
Use HOSTKIT_INCUS_TYPE=vm for an Incus VM instead of the default container.
Real remote validation
Copy examples/integration_hosts.example.exs, set the hostname/auth settings, then run:
HOSTKIT_SSH_PASSWORD='...' \
HOSTKIT_INTEGRATION_TOOL=remote \
HOSTKIT_INTEGRATION_CONFIG=examples/integration_hosts.example.exs \
mix test test/integration/cli_remote_test.exs --include integration