Skip to content

CLI & althost.yaml

The althost CLI (cli/) deploys services declaratively from an althost.yaml in a directory. It authenticates with an API key.

Install

The install is one line. It downloads and installs the latest CLI (like the runner installer):

curl -s "https://api.althost.dev/v1/cli/script" | sh

This fetches cli.zip. It installs its deps. It puts althost at ~/.local/bin/althost. Add that dir to your PATH if it is not there already. You can configure credentials in the same step:

curl -s "https://api.althost.dev/v1/cli/script" | sh -s -- \
  --server https://api.althost.dev --api-key ak_xxx

Or from a checkout:

cd cli && npm install
npm link          # exposes `althost` on your PATH (or run: node cli/index.mjs)

Configure

Credentials resolve in this order: flags → env (ALTHOST_SERVER / ALTHOST_API_KEY) → ~/.althost/cli.json.

althost config --server https://api.althost.dev --api-key ak_xxx

This writes ~/.althost/cli.json (mode 600). The key needs the services:deploy scope. It also needs services:read for status.

althost.yaml

staticApps:
  site:                        # a prebuilt dir — uploaded by `deploy`
    path: ./dist
    node: my-computer
  app:                         # built locally by `build`
    build:
      command: npm run build   # runs in the project dir
      output: dist             # the dir uploaded after the build
    node: my-computer
services:
  api:
    image: myorg/api:1.2.3
    replicas: 2         # optional, default 1
    memory: 512         # optional MB, default 256
    node: my-computer   # optional: node id or label to run on
  worker:
    image: myorg/worker:latest
    memory: 256
containers:
  cache: { image: redis:7, port: 6379, node: my-computer }
databases:
  main: { engine: postgres, version: "16", tier: dev, node: my-computer }

Commands

althost build [dir]      # run each static app's build command, then upload its output
althost deploy [dir]     # deploy <dir>/althost.yaml (default: current directory)
althost services         # list services and their status
althost logs <name> [-f] # tail a service/container's logs (--follow to stream)
althost status           # show current resource state
althost config ...       # save server/api-key
althost logout           # remove saved credentials (~/.althost/cli.json)
althost cleanup          # remove credentials + leftover temp build dirs
althost help

logs resolves <name> against your services then containers. It prints recent output. --follow (-f) keeps polling for new lines. Both services and logs need a key with the read scope.

deploy is idempotent. It creates new resources and updates existing ones by name. It prints created / updated / unchanged per resource. A changed image bumps the service version so the runner redeploys it. It uploads any static app path dirs as-is. They must already be built.

$ althost deploy .
Deploying 2 service(s) from /app/althost.yaml
  • api updated (v3, provisioning)
  • worker created (v1, provisioning)
Done.

Building static apps locally

build is the local-build path. For each static app it either:

  • has a build: block — it runs command in the project directory. It then zips and uploads the resulting output dir. You need not commit a prebuilt directory; or
  • has only a path: — it zips and uploads that prebuilt dir as-is.

Either way it uploads a new version. The runner downloads and serves that version. Both are driven entirely by the yaml. The key needs static-apps:deploy.

$ althost build .
Building 1 static app(s) from /app/althost.yaml
  $ npm run build
  ↑ app built + uploaded (v4)
Done.

Building service & container images locally

A build: block on a service or container builds a container image locally. It uploads the image as an artifact. The build requires no registry. build runs docker build on the context. It runs docker save on the image. It declares the resource (its image is just the build tag). It uploads the tarball. The runner downloads the latest artifact. It loads the artifact and deploys it. Containers use a blue-green deploy. A failed load or health-check keeps the previous version serving.

services:
  api:
    build:
      context: .            # dir containing the Dockerfile (default ".")
      dockerfile: Dockerfile # optional
      tag: myorg/api:local   # optional; default althost-local/<name>:latest
    node: my-computer
containers:
  web:
    build: { context: ./web }
    port: 8080
    node: my-computer
$ althost build .
Building 1 target(s) from /app/althost.yaml
  $ docker build -t althost-local/web:latest ./web
  ↑ containers/web built + uploaded (v3, althost-local/web:latest)
Done.

The builder is docker by default. Set ALTHOST_DOCKER (e.g. nerdctl, podman) to match your local runtime. The key needs services:deploy / containers:deploy.

Large images upload in chunks automatically. Any image over ALTHOST_ARTIFACT_CHUNK_MB (default 32 MB) splits into chunks. Each chunk uploads and retries independently. A single network blip does not restart the whole upload. A proxy's request-body limit does not bound the transfer. Smaller images use a single request.

Retention: the control plane keeps the newest ARTIFACT_RETENTION (default 5) versions per target. It drops anything older than ARTIFACT_RETENTION_DAYS (default 7). Whichever prunes more applies. Old tarballs do not accumulate. The currently-deployed (latest) version is always kept, even if it is older than the age limit. The system enforces the count cap on each upload. The age cap runs on a daily sweep.

This is the local-build path of althost's build/artifact pipeline. Automated git-triggered pipelines (a builder agent on a dedicated machine) build on the same build → artifact → deploy loop.