Installation
Add the SDK to your project, or host the standalone server and dashboard yourself.
How you install Aiki depends on what you're doing:
- Building workflows — add the SDK to your TypeScript project and run the server embedded in your app, or point your app at a hosted Aiki server. Follow Add Aiki to your project.
- Hosting Aiki — run the standalone server and web dashboard as infrastructure, with no SDK install. You need a database: a SQLite file, or a PostgreSQL server. Run the server from the prebuilt
aikibinary, or run both from the published Docker images or from source with Bun. Follow Run the standalone server and dashboard.
Add Aiki to your project
You need Node.js 22.16+ or Bun 1.4+, and a database. Aiki ships as ESM only, so your project must be ESM.
Aiki stores its state in one of two databases. You pick one with DATABASE_PROVIDER:
| Database | DATABASE_PROVIDER | Where it lives | Also set |
|---|---|---|---|
| SQLite | sqlite | a single file on the Aiki server's disk | DATABASE_PATH |
| PostgreSQL 14+ | pg | a Postgres server you run | DATABASE_URL |
SQLite needs nothing installed, so it's the quickest way to start. A SQLite file serves one server process. Two processes on the same file, such as two servers or two apps that each embed one, can stall each other. To run several server instances, use Postgres.
Install the SDK packages
npm install @aikirun/workflow @aikirun/client @aikirun/worker @aikirun/serverSQLite needs no driver: Aiki uses the SQLite built into Node.js and Bun.
npm install @aikirun/workflow @aikirun/client @aikirun/worker @aikirun/server postgrespostgres is the driver for Postgres.
@aikirun/server declares postgres as an optional peer dependency, so your package manager does not install it on its own.
Apply the schema migration
If you will run the standalone server, skip this step — that section covers migration for each way of running it.
DATABASE_PROVIDER=sqlite DATABASE_PATH=./aiki.db \
npx aiki-server migrate applyThe file is created if it doesn't exist.
On Node 22 and 24, this command and your app print ExperimentalWarning: SQLite is an experimental feature and might change at any time. It is Node's notice about its built-in SQLite.
DATABASE_PROVIDER=pg DATABASE_URL=postgresql://user:password@your-db-host:5432/aiki \
npx aiki-server migrate applyRe-run this command when upgrading Aiki to apply new migrations.
Run the server
Serve the Aiki server's handler with any HTTP framework, in the same process as your app or in a process dedicated to Aiki. The Quick Start walks through this end to end, including the worker and client.
An embedded server should also be given a timer priority queue. Without one, a run can wait several seconds before it moves on: after it starts, after an event arrives, or when a sleep or retry comes due.
npm install @aikirun/memoryimport { inMemoryTimerPriorityQueue } from "@aikirun/memory";
const aikiServer = server({
db: database({ provider: "sqlite", path: "./aiki.db" }),
timerPriorityQueue: inMemoryTimerPriorityQueue(),
});import { inMemoryTimerPriorityQueue } from "@aikirun/memory";
const aikiServer = server({
db: database({ provider: "pg", url: "postgresql://user:password@your-db-host:5432/aiki" }),
timerPriorityQueue: inMemoryTimerPriorityQueue(),
});The in-process queue serves one server instance. Across several instances use @aikirun/redis, which hands each timer to exactly one of them — see Server.
Workflow code is identical against an embedded or standalone server, so you can also skip embedding entirely and point your client at a server hosted per Run the standalone server and dashboard.
Want the web dashboard for your embedded server? Serve the server's handler over HTTP, as the Quick Start does, then point the dashboard at it. See Run the dashboard on its own.
Run the standalone server and dashboard
Hosting Aiki as infrastructure means running two components:
- Server — the Aiki server. Needs
DATABASE_PROVIDER, plusDATABASE_PATHfor SQLite orDATABASE_URLfor Postgres. - Dashboard — the web UI, a single-page app that talks to the server.
However you run the server, applications connect the same way:
const aikiClient = client({ url: "http://localhost:9850" });Before the server starts, apply the schema. This one-off migration step applies the server package's migrations, plus the iam package's when the server runs with auth (AIKI_SERVER_AUTH_SECRET). It needs the same database settings as the server, and is safe to repeat: run it on a fresh database or when an aiki upgrade ships new migrations. Already-applied migrations are skipped.
There are three ways to run them:
- The
aikibinary — runs the server from one downloaded executable with its own runtime, no Node, Bun, or Docker to install. The dashboard is served separately. - Docker — run the published images, brought up together by Compose or on their own. Best if you already run containers.
- From source — clone at a release tag and run with Bun. Best for contributors.
With the aiki binary
Download the binary for your platform from the latest release. It carries the migrate and server commands plus its own runtime. Assets are published for macOS on Apple Silicon (aiki-darwin-arm64) and Linux (aiki-linux-arm64, aiki-linux-x64); on any other platform use Docker or From source instead.
The asset is named for its platform and downloads without the executable bit, so rename it and make it executable before putting it on your PATH:
mv aiki-<platform> aiki
chmod +x aikiIf macOS then refuses to run it, see Troubleshooting.
export DATABASE_PROVIDER=sqlite
export DATABASE_PATH=./aiki.dbexport DATABASE_PROVIDER=pg
export DATABASE_URL=postgresql://user:password@your-db-host:5432/aikiaiki migrate apply # migrates the server package; use --package server,iam to include iam
aiki server start # serves on 127.0.0.1:9850Both commands read configuration from the environment; pass --env-file <path> to load it from a file instead.
The server accepts connections from this machine only. To serve other machines, set AIKI_SERVER_HOST=0.0.0.0, and turn on authentication with AIKI_SERVER_AUTH_SECRET and AIKI_SERVER_BASE_URL. Without it, anyone who can reach the server can start and read runs.
The dashboard ships separately — serve it as a static site (see Run the dashboard on its own) or from a container (see With Docker). A dashboard on a different origin than the server needs its origin added to the server's CORS_ORIGINS (via --env-file or the environment).
With Docker
There are three published images: ghcr.io/aikirun/cli — the same aiki binary — runs the migration, ghcr.io/aikirun/server runs the server, and ghcr.io/aikirun/dashboard serves the web UI. Compose wires them together; you can also run them yourself.
With Docker Compose
Download the standalone compose file from the latest release into an empty directory — it pulls that release's published images, so there is nothing to clone or build:
mkdir aiki && cd aiki
curl -fsSL https://github.com/aikirun/aiki/releases/latest/download/docker-compose.yml -o docker-compose.ymlCreate a .env next to it that picks the database:
DATABASE_PROVIDER=sqliteThe database file lives on a Docker volume, aiki-data, so it survives restarts and upgrades.
DATABASE_PROVIDER=pg
DATABASE_URL=postgresql://user:password@your-db-host:5432/aikiRunning Postgres on the same machine as Docker? See Troubleshooting.
Compose also reads these variables from your shell or from --env-file. Every docker compose command needs DATABASE_PROVIDER, including down and logs, and stops with a message saying so when it's missing. A .env next to the compose file keeps it set for all of them.
docker compose up -d- Server: http://localhost:9850
- Dashboard: http://localhost:9851
The migration step applies the packages listed in AIKI_MIGRATE_PACKAGES — by default server, plus iam when AIKI_SERVER_AUTH_SECRET is set. Override the list to depart from that, for example to create the iam tables before turning auth on: AIKI_MIGRATE_PACKAGES=server,iam docker compose up -d.
Without Docker Compose
Run the images yourself when you orchestrate containers with your own tooling. Migrate first, then start the server against the same database:
The file sits on a volume that both containers mount, so it outlives them:
# migration
docker run --rm -v aiki-data:/data -e DATABASE_PROVIDER=sqlite -e DATABASE_PATH=/data/aiki.db \
ghcr.io/aikirun/cli:<version> migrate apply --package server
# server
docker run -p 9850:9850 -v aiki-data:/data -e DATABASE_PROVIDER=sqlite -e DATABASE_PATH=/data/aiki.db \
ghcr.io/aikirun/server:<version># migration
docker run --rm -e DATABASE_PROVIDER=pg -e DATABASE_URL=postgresql://user:password@your-db-host:5432/aiki \
ghcr.io/aikirun/cli:<version> migrate apply --package server
# server
docker run -p 9850:9850 -e DATABASE_PROVIDER=pg -e DATABASE_URL=postgresql://user:password@your-db-host:5432/aiki \
ghcr.io/aikirun/server:<version>Then start the dashboard, which proxies browser calls to the server:
docker run -p 9851:9851 -e AIKI_SERVER_UPSTREAM_URL=http://your-server:9850 \
ghcr.io/aikirun/dashboard:<version>Add --package server,iam to the migration when the server runs with auth. The dashboard image proxies to the server, so it needs no CORS_ORIGINS; to serve the dashboard from a static host instead, see Run the dashboard on its own. Pick versions from the releases.
From source
The server and dashboard also run without Docker (this path needs Bun). Clone at a release tag so you run released code — pick one from the releases:
git clone --branch <version> https://github.com/aikirun/aiki.git
cd aiki
bun install
cp app/server/.env.example app/server/.env
# Edit app/server/.env with your database settings
bun run --cwd app/server db:migrate:apply:server # the migration step
bun run server # Terminal 1
bun run dashboard # Terminal 2Run bun run --cwd app/server db:migrate:apply:iam too when the server will run with auth. One piece differs from its container form: the dashboard here is a dev server, and the browser calls the Aiki server directly on localhost:9850 — no proxy and no AIKI_SERVER_UPSTREAM_URL (the .env.example already allows the dashboard's origin through CORS_ORIGINS).
Run the dashboard on its own
Serve the dashboard by itself when the server is embedded in your app (the SDK path above) or when you host the server another way. It is a single-page app that calls the server from the browser. Serve it as a static site; to run it as a container instead, use the dashboard image under With Docker.
The server's URL is baked into the bundle at build time, so the dashboard is built per deployment (the build needs Bun) — from the release tag matching your installed @aikirun/* version:
git clone --branch <version> https://github.com/aikirun/aiki.git
cd aiki
bun install
bun run build:types
VITE_AIKI_SERVER_URL=https://aiki.example.com bun run build:dashboardDeploy app/dashboard/dist/ to any static file host. Because the browser calls the server cross-origin, two things must line up: the build sets VITE_AIKI_SERVER_URL to the server's URL, and the server's CORS_ORIGINS allows the dashboard's origin. Miss either and the dashboard fails loudly in the browser — its error screen names both.
Environment Variable Reference
These apply to the standalone server and the dashboard. If you embed the server in your own app, you control configuration directly via the server({...}) factory and don't need these.
Server
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_PROVIDER | Yes | — | sqlite for SQLite or pg for Postgres |
DATABASE_PATH | For SQLite | — | Path to the SQLite file; created if it doesn't exist |
DATABASE_URL | For Postgres | — | Postgres connection string |
DATABASE_MAX_CONNECTIONS | No | driver default | Postgres connection pool size |
DATABASE_CA_CERT | No | — | PEM contents of a CA certificate used to verify Postgres's TLS certificate. Needed to verify against a private CA (e.g. DigitalOcean, RDS) |
AIKI_SERVER_HOST | No | 127.0.0.1 | Address the server listens on. 127.0.0.1 accepts connections from this machine only; 0.0.0.0 accepts them from any machine that can reach it. The Docker images set 0.0.0.0 |
AIKI_SERVER_PORT | No | 9850 | Server port |
AIKI_SERVER_BASE_URL | If IAM is on | — | Public URL of the server; required only when AIKI_SERVER_AUTH_SECRET is also set |
AIKI_SERVER_AUTH_SECRET | If IAM is on | — | Authentication secret; setting this (with AIKI_SERVER_BASE_URL) activates the IAM package |
CORS_ORIGINS | If the dashboard is cross-origin | — | Comma-separated allowed origins, e.g. a static-host dashboard's; unneeded behind the dashboard image's proxy |
REDIS_URL | No | — | Redis connection string, e.g. redis://localhost:6379; enables Redis-backed work distribution and moves timer dispatch from the in-process queue to a shared Redis-backed one |
LOG_LEVEL | No | info | Log level |
PRETTY_LOGS | No | false | Pretty-print logs; false prints JSON |
Dashboard
| Variable | Required | Default | Description |
|---|---|---|---|
AIKI_SERVER_UPSTREAM_URL | If using the docker image | — | Server address the dashboard docker image proxies to; read at container start |
AIKI_DASHBOARD_HOST | No | 0.0.0.0 | Address the dashboard docker image listens on. Set 127.0.0.1 when the container runs with --network host, so it accepts connections from this machine only. Keep the default when you publish the port with -p |
AIKI_DASHBOARD_PORT | No | 9851 | Port for the dashboard |
VITE_AIKI_SERVER_URL | If on a static host | — | Build-time server URL for a dashboard served outside the docker image |
Which variable applies depends on how you serve the dashboard: the docker image reads AIKI_SERVER_UPSTREAM_URL (see With Docker), and a static-host build reads VITE_AIKI_SERVER_URL (see Run the dashboard on its own). The bundled docker-compose.yml sets AIKI_SERVER_UPSTREAM_URL for you.
Troubleshooting
macOS won't run the binary. macOS quarantines anything downloaded with a browser. Clear the quarantine, using the name you gave the binary:
xattr -c aikiDownloads fetched with curl are not quarantined.
The server can't reach your database. The server runs in a container, so localhost in the DATABASE_URL you set names that container — not your machine. When Postgres runs on your machine, use host.docker.internal as the host:
DATABASE_URL=postgresql://user:password@host.docker.internal:5432/aikiThe bundled docker-compose.yml already maps that name to the host. If you run the images yourself, add --add-host=host.docker.internal:host-gateway to each docker run.
Next Steps
- Quick Start — Create your first workflow
- Workflows — Deep dive into workflow concepts