Skip to content

Installation

Leoflow ships in two editions โ€” pick the install path that matches the one you want:

Edition Where it runs Who it's for Install path
Lite Your laptop or a single VM (no Kubernetes, no Docker required) Local development, small teams, evaluation Install Lite
Pro A Kubernetes cluster (pod-per-task executor) Team-scale and production workloads Install Pro

See Editions for the full feature-by-feature breakdown. The two editions share the same Go control plane and the same Airflow-compatible HTTP API โ€” Pro adds the K8s executor, HA scheduler, and external-datastore expectations; Lite bundles everything in one host process.


Install Lite

One command installs Leoflow Lite and bootstraps everything it needs โ€” no sudo, no system Python, no package manager:

curl -fsSL https://raw.githubusercontent.com/neochaotic/leoflow/main/install.sh | sh

That script downloads the release archive for your OS/architecture, verifies its SHA-256 against the signed checksums, installs the binaries to ~/.leoflow/bin, and then runs leoflow setup.

What you need

Almost nothing. The control plane, CLI, and agent are static Go binaries, and leoflow setup provisions a Python 3.11 itself if you don't have one.

There are two execution paths โ€” and no Docker executor, on purpose (ADR 0015): the Docker Go SDK carries an unfixable advisory (Moby AuthZ bypass, GO-2026-4887) that would reach the control-plane binary and fail the security gate. So:

Executor Needs Isolation For
subprocess just the install (binaries + a managed Python) none (dev-only) fast local iteration, small projects
kubernetes + Docker (to host a local k3d cluster; k3d/kubectl fetched on demand) real pods production parity, the staging volume, resource limits

Docker, when present, is only the engine that hosts the local k3d cluster โ€” it is never an executor itself. leoflow setup detects what's present and picks the highest path available; without Docker it uses subprocess. Run leoflow doctor anytime to see where you stand, and see Choosing an executor for the trade-offs.

What leoflow setup does

setup is idempotent โ€” re-running is safe. It:

  1. Ensures Python 3.11. Uses a system python3.11 if one is on PATH; otherwise downloads a pinned, checksum-verified relocatable CPython into ~/.leoflow/python. No sudo, no system install.
  2. Extracts the DAG parser and task runtime (embedded in the binary) to ~/.leoflow/pysrc.
  3. Points parser_cmd at the parser in ~/.leoflow/config.yaml. The parser is pure Python with its dependencies vendored (the Airflow shim and PyYAML โ€” ADR 0024), so there is no parser venv, no pip, and no Apache Airflow install โ€” it runs on the interpreter from step 1 directly.
  4. Creates your workspace (default ~/leoflow, override with --workspace) for your DAG projects, and asks (on a terminal) for the workspace, executor (subprocess for local use, k8s for a dev mini-cluster โ€” changeable later), and UI port. Run non-interactively (e.g. curl | sh) it uses sensible defaults.
  5. Creates the Lite admin (admin@leoflow.local) with a generated, human-friendly password, shown once at the end (only its hash is stored). Recover it with leoflow lite reset-password.

Lite is for trusted networks

The admin password is short by design and there is no SSO/RBAC โ€” run Lite on localhost, an internal network, or a VPN, never exposed publicly. Production-grade deploys are Pro's job. See Editions.

Everything Leoflow manages lives under ~/.leoflow; your DAG source lives in the workspace โ€” the two are kept separate.

leoflow setup                      # interactive on a terminal; defaults otherwise (safe to re-run)
leoflow setup --dry-run            # show the plan, change nothing
leoflow setup --workspace ~/work   # choose where your DAG projects live

There is no scanned dags/ folder

Unlike Airflow, Leoflow has no monolithic DAGs directory. Each DAG is its own project (dag.py + leoflow.yaml); you point leoflow lite <path> at it. The workspace is just a convenient home for those projects.

Platforms

Leoflow ships Linux and macOS binaries for amd64 and arm64. Because the install never touches your system package manager, the Linux distribution does not matter โ€” only the C library and CPU architecture do:

  • glibc distros (Ubuntu, Debian, Fedora, RHEL/Rocky/Alma, Arch, openSUSE) and musl (Alpine) are both supported; setup detects musl and fetches the matching CPython build.
  • Windows: use WSL2 (it's a glibc Linux). Keep your project in the WSL native filesystem (~/...), not under /mnt/c โ€” leoflow lite's hot-reload uses inotify, which is unreliable on the Windows 9p mount. leoflow doctor warns when your project is under /mnt.

Verifying the download

The release publishes checksums.txt (SHA-256), and the checksums file is cosign-signed (keyless). install.sh verifies the archive checksum automatically. To verify the signature yourself:

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity-regexp 'https://github.com/neochaotic/leoflow' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  checksums.txt

leoflow doctor

A read-only diagnostic โ€” it changes nothing:

$ leoflow doctor
leoflow doctor

  platform      linux/amd64 (glibc)
  python 3.11   found (/usr/bin/python3.11)
  docker        found
  k3d           not found (fetched on demand for the k8s tier)
  kubectl       not found (fetched on demand for the k8s tier)

  recommended executor: k8s
    subprocess  always available (dev-only, no isolation)
    kubernetes  available (Docker present; k3d/kubectl fetched on demand)

  next: run `leoflow setup` to bootstrap the managed runtime.

Installer options

Variable Effect
LEOFLOW_VERSION=v0.0.1-prealpha.1 install a specific release (default: newest, including pre-releases)
LEOFLOW_NO_SETUP=1 install binaries only; run leoflow setup yourself later
LEOFLOW_INSTALL_DIR=~/.leoflow/bin where to put the binaries

Building Lite from source

If you have a Go toolchain and prefer to build it yourself:

go install github.com/neochaotic/leoflow/cmd/leoflow@latest
go install github.com/neochaotic/leoflow/cmd/leoflow-server@latest
go install github.com/neochaotic/leoflow/cmd/leoflow-agent@latest
# ensure $(go env GOPATH)/bin is on your PATH, then:
leoflow setup

The subsequent leoflow setup provisions the same managed runtime the install-script path uses (managed CPython under ~/.leoflow/).

Uninstalling Lite

Use the built-in command โ€” it removes the install directory and (with --purge) your workspace too:

leoflow uninstall              # removes ~/.leoflow (binaries, managed Python, parser, config)
leoflow uninstall --purge      # also removes ~/leoflow (your DAGs!)

If the leoflow binary is gone or broken, fall back to the same paths by hand:

rm -rf ~/.leoflow              # what `leoflow uninstall` would have removed
rm -rf ~/leoflow               # what `--purge` adds (your workspace)

Install Pro

Pro installs the control plane into Kubernetes via the Leoflow Helm chart. Task pods are scheduled into the cluster by the same control plane โ€” no host-side process supervisor, no managed Python sidecar. DAGs ship as container images built in CI (CI/CD & deploy examples).

What you need

Requirement Why
A Kubernetes cluster (1.27+ recommended) runs the control plane and task pods
kubectl configured against that cluster applies the chart and lets the chart's pre-install Job run migrations
Helm 3.12+ installs the chart
An external Postgres (PostgreSQL 13+) Pro datastore โ€” the chart refuses to install without database.url (the embedded datastore is Lite-only)
An external Redis (Redis 6.0+) XCom + advisory locks โ€” the chart refuses to install without redis.url

Managed services are first-class โ€” RDS / Cloud SQL / Azure Database for Postgres on the SQL side; ElastiCache / Memorystore / Azure Cache for Redis. See the chart's Datastore compatibility table for tested versions; managed providers that present a per-instance or provider-specific CA expose a caConfigMap knob (Postgres and Redis sides respectively) for verified TLS.

No managed datastore yet? There's a PoC path.

For a one-cluster evaluation (kind, minikube, k3d, scratch namespace), the chart deliberately won't fall back to embedded datastores โ€” that's Lite's job. The supported PoC path is to install plain Postgres + Redis manifests alongside the chart, then point Leoflow at the in-cluster Services. Recipe: helm/leoflow/examples/README.md. Not for production.

Install with Helm

The chart is published per release (and built from helm/leoflow in the repo). For pre-alpha cadence, the simplest path is to install directly from the cloned repo at a checked-out tag:

git clone --depth 1 --branch v0.0.1-prealpha.27 https://github.com/neochaotic/leoflow
cd leoflow

kubectl create namespace leoflow

helm install lf ./helm/leoflow -n leoflow \
  --set image.tag=v0.0.1-prealpha.27 \
  --set migrations.image.tag=v0.0.1-prealpha.27 \
  --set database.url='postgres://USER:PASS@HOST:5432/leoflow?sslmode=verify-full' \
  --set redis.url='rediss://HOST:6380/0' \
  --set auth.jwtSecret="$(openssl rand -base64 64)" \
  --set secretKey="$(openssl rand -hex 16)" \
  --set bootstrap.password='change-me'

What this installs (one Deployment, one Service, RBAC for the pod-per-task executor, a pre-install/upgrade migrations Job; optional Ingress, PDB, HPA, ServiceMonitor, NetworkPolicy):

  • leoflow-server Deployment listening on HTTP 8080, metrics 9090, and agent gRPC 9091.
  • A pre-install/pre-upgrade Job running golang-migrate against database.url before the server starts.
  • A ServiceAccount + Role/RoleBinding letting the control plane create, watch, and delete task pods (and read their logs) in taskNamespace.
  • A chart-managed Secret holding the inline DB / Redis / JWT / bootstrap credentials. Skipped when you bring your own via *.existingSecret.

Open the UI by port-forwarding the Service, or enable ingress.enabled=true with a controller of your choice โ€” see the chart's ingress values for the field shape. Log in as the bootstrap admin (admin@leoflow.local / the password you set above) and rotate it.

Bring-your-own Secrets

Inline --set values bake credentials into the chart-managed Secret. Production deploys typically pre-create Secrets (sealed-secrets, External Secrets, etc.) and point the chart at them:

--set database.existingSecret=my-db     # key: databaseUrl
--set redis.existingSecret=my-redis     # key: redisUrl
--set auth.existingSecret=my-jwt        # key: jwtSecret
--set secretKeyExistingSecret=my-key    # key: secretKey
--set bootstrap.existingSecret=my-boot  # key: bootstrapPassword

When every credential comes from an existing Secret, the chart creates no Secret of its own. The checksum/secret annotation on the pod template only sees the chart-managed Secret, so rotation of an existingSecret requires a manual kubectl rollout restart deploy/lf-leoflow.

Upgrades

helm upgrade runs the migrations Job, rolls the Deployment, and respects PDB/replica settings. The full upgrade contract โ€” version skew, downtime expectations, rollback โ€” lives in Upgrades.

Verifying the chart and images

Both the chart and the images (leoflow-server, leoflow-migrate, plus leoflow and leoflow-agent binaries) are published by .github/workflows/release.yaml and cosign-signed (keyless):

# Verify the server image at a release tag.
cosign verify ghcr.io/neochaotic/leoflow-server:v0.0.1-prealpha.27 \
  --certificate-identity-regexp 'https://github.com/neochaotic/leoflow' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Full values reference

The chart README on this site is auto-generated from values.yaml by helm-docs and documents every knob โ€” TLS, observability, networking, autoscaling, secret wiring. Treat it as the source of truth.

Uninstalling Pro

helm uninstall lf -n leoflow
kubectl delete namespace leoflow

This removes the chart-managed resources. PVCs (e.g. for control-plane logs when logs.persistence.enabled=true) and any external Postgres / Redis data outlive the chart โ€” drop them out of band when you're done.


Next