Skip to content

Install

The installer detects your hardware, picks a backend (Vulkan, ROCm, CUDA, or CPU), writes the systemd units, drops the hal0 CLI on PATH, and brings up the API plus a prewired OpenWebUI tab. It’s idempotent. Re-runs touch nothing in /etc/hal0/.

Terminal window
curl -fsSL https://hal0.dev/install.sh | bash

hal0 doesn’t care which slice of your homelab it runs on, as long as the kernel speaks systemd and Docker. Three shapes worth naming:

  • Bare-metal Linux. The simplest case. hal0-api, hal0-openwebui, and the per-slot toolbox containers all live on the host.
  • VM. Works if you give it enough RAM and pass the GPU through. Eats more host memory than an LXC for the same workload.
  • Privileged LXC on Proxmox (recommended for homelabs). GPU and NPU passthrough via dev0dev3 + cgroup allows. The Strix Halo iGPU + XDNA recipe is privileged + AppArmor unconfined; AMD discrete
    • ROCm needs /dev/kfd and /dev/dri/* passed through. CPU-only is the one shape that runs fine unprivileged. The unified-memory bar in the dashboard surfaces the LXC’s cgroup slice and, when you wire in a PVEAuditor token via /etc/hal0/proxmox.json, the physical host total plus other tenants. hal0 knows it isn’t the only thing on the box.
hal0 dashboard at / showing KPI row (API, Slots, Memory, Throughput), the unified-memory bar with GTT inference and Proxmox host segments, and the slot grid below.

Dashboard at /. Four KPIs across the top (API, Slots, Memory, Throughput), the unified-memory bar, then the slot grid. On a fresh install the slots sit offline until the first-run wizard or the CLI loads one.

  1. Linux with systemd. Tested on Arch, CachyOS, Fedora, and Ubuntu 22.04+. macOS and Windows are not in scope for v1.

  2. x86_64. ARM is not currently supported.

  3. Docker reachable. The slot toolboxes run as containers. The installer checks docker ps before doing anything destructive.

  4. At least 20 GB free under /var/lib. Models, registry, and OpenWebUI state land there. Override via --models-dir=/path if you’d rather point at a NAS mount or an existing model store.

  5. Ports 8080 and 3001 free. Override with HAL0_PORT and HAL0_OPENWEBUI_PORT if you need different ones.

  1. Pre-flight checks. Verifies systemd, x86_64, Docker, free space, and free ports. Bails before touching anything if a check fails. Re-runnable on its own as hal0 doctor.

  2. Hardware probe. Detects GPU, NPU, and unified-memory pools. Writes /etc/hal0/hardware.json. Drives the default backends and the slot-fit warnings in the dashboard.

  3. FHS-aligned layout. Creates the tree the rest of hal0 expects:

    • /usr/lib/hal0/current/ — versioned code, atomic symlink swapped by hal0 update
    • /etc/hal0/ — config (hal0.toml, api.env, openwebui.env, slot skeletons), preserved across updates
    • /var/lib/hal0/ — models, registry, slot state, OpenWebUI state
  4. Config defaults. Writes hal0.toml and the five built-in slot skeletons (primary, embed, stt, tts, img). The hardware recommender renders a primary.toml with enabled = false so you pull a model and flip it on yourself. Existing files are never overwritten on re-run.

  5. systemd units. Drops hal0-api.service, hal0-openwebui.service, and the hal0-slot@.service template into /etc/systemd/system/. Reloads the daemon, enables and starts the API plus OpenWebUI.

  6. hal0 on PATH. Symlinks /usr/local/bin/hal0${VENV_DIR}/bin/hal0 so the CLI works without sourcing anything. uninstall.sh removes the symlink.

  7. Finish. Prints reachability URLs, streams a live “hello” through the freshly-spawned slot, and (if qrencode is on PATH) drops an ANSI QR pointing at the dashboard.

Unified-memory bar with segments labelled System RAM, GTT (iGPU inference), Proxmox host pressure, and Free.

The memory bar reads physical-host totals when it can, instead of the LXC’s cgroup slice in isolation. On a Proxmox LXC, hand the dashboard a read-only PVEAuditor API token plus endpoint in Settings → Proxmox integration and the bar adds a muted “Proxmox host” segment for other tenants, ZFS ARC, and kernel pressure. Token and endpoint live 0600 at /etc/hal0/proxmox.json and the API redacts the value on read and in logs. Bare-metal installs leave the panel off and the bar stays scoped to the local box. Segment order is System RAM | GTT (iGPU inference) | Proxmox host pressure | Free.

Every knob is an environment variable. Pass them on the same line as the installer:

Terminal window
HAL0_PORT=9090 HAL0_OPENWEBUI_PORT=9091 \
curl -fsSL https://hal0.dev/install.sh | bash
VariableDefaultPurpose
HAL0_PREFIX/usr/lib/hal0Install prefix
HAL0_PORT8080API + dashboard port
HAL0_OPENWEBUI_PORT3001OpenWebUI port
HAL0_USERhal0Service user
HAL0_PYTHONpython3Python interpreter
HAL0_NO_PROBEunsetSkip the hardware probe
HAL0_AUTO_PULL0Pre-pull toolbox images on install
HAL0_TOOLBOX_IMAGE_VULKANrepo defaultOverride Vulkan toolbox tag
HAL0_TOOLBOX_IMAGE_ROCMrepo defaultOverride ROCm toolbox tag

HAL0_HOME=$PWD/.hal0 relocates the whole tree under your working directory and skips the systemd path entirely. Useful for dev installs.

install.sh --models-dir=/srv/models points the install at an existing model store at provision time. Resolution order: explicit flag, then HAL0_MODELS_DIR, then an interactive prompt on a tty, then /var/lib/hal0/models. The path is persisted as [models].pull_root and auto-included in [models].roots so a fresh install scans the existing tree on first boot.

The default install has no auth in front. Fine on a trusted home LAN behind your Traefik or Caddy. For exposed deployments add --auth=basic:

Terminal window
sudo bash installer/install.sh --auth=basic

The installer prompts for an admin user and password, installs Caddy, generates a TLS cert (self-signed for .local hostnames, Let’s Encrypt for real domains), mints a Bearer token, and round-trips https://${HAL0_HOSTNAME}/api/health as a self-test before exiting. See Authentication & HTTPS for the full flow including client-side cert trust and ACME setup.

Terminal window
git clone https://github.com/hal0ai/hal0
cd hal0
sudo bash installer/install.sh

The script in the repo is the same one the URL serves.

Terminal window
hal0 status

System summary plus the state of every slot. A healthy install shows the five built-in slots offline; the first-run wizard lights up primary for you.

Terminal window
curl http://localhost:8080/v1/models

Returns data: [] until you’ve assigned a model to a slot.

Terminal window
hal0 doctor

Re-runs the pre-flight pack against the live host. Handy after a kernel upgrade, a Docker daemon swap, or whenever something feels off.

Terminal window
sudo bash /usr/lib/hal0/current/installer/uninstall.sh

Stops the services, removes the unit files, and (with --keep-data) leaves config and models in place. Without --keep-data it also clears /etc/hal0 and /var/lib/hal0.