Browse docs
Docs /install/self-hosting
Install

Self-hosting

Izri is open-core under AGPL-3.0. The four signals, the CLI, the MCP server, and the GitHub Action all run against a self-hosted instance with no license key.

You need the runner

Start here, because it is the failure everyone hits.

Izri is five services, not two. The API and web app alone look like a working stack — the dashboard loads, you can create a project, you can trigger a run. But triggering a run only enqueues a Redis job. With no runner draining that queue, the run sits at PENDING forever and nothing ever tells you why.

The API does warn: a runner-presence watchdog logs a WARN once the queue stays non-empty for more than 60 seconds. If a run is stuck, check the API logs first.

Services

Service Port Purpose
web 3000 React Router SSR dashboard
api 4000 Hono server, tRPC, webhooks
ai-service 8000 Python FastAPI — analysis and generation
runner Drains the Redis queue and executes tests
postgres 5433 Primary datastore
redis 6380 Job queue

Postgres is on 5433 and Redis on 6380, not their defaults, so a self-hosted instance doesn't collide with a database already running on the host.

Bring it up

git clone https://github.com/legendify-dev/izri.git
cd izri
cp .env.shared.example .env.shared
# edit .env.shared — see below
pnpm install
pnpm env:generate
docker compose up -d

pnpm env:generate is not optional. It writes the per-app .env files from .env.shared; without it auth, the database layer, and tRPC fail in confusing ways rather than reporting a missing variable.

Environment

The variables that actually block a first run:

Variable Notes
DATABASE_URL Postgres connection string.
REDIS_URL Queue connection.
API_URL / APP_URL Must match where the services are actually reachable.
BETTER_AUTH_SECRET Change it. The example value is a placeholder.
JWT_SECRET Change it too.
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET OAuth login.
GITHUB_APP_ID / GITHUB_APP_PRIVATE_KEY / GITHUB_APP_WEBHOOK_SECRET Required for PR checks and the sticky comment.
AI_PROVIDER openrouter, openai, anthropic, or mock.

The OAuth callback will bite you

The GitHub OAuth callback is derived from your configured app URL. If the URL your browser uses and the URL Izri is configured with disagree — a port mismatch is the usual cause — login fails as a silent redirect loop with no error message. Make them match exactly.

AI provider

Set AI_PROVIDER explicitly. mock runs the deterministic layers with no external calls and no cost, which is the right choice for evaluating the stack. The semantic scope layer and LLM insights need a real provider.

Point your tooling at it

Everything takes the same override:

export IZRI_API_URL=https://izri.internal.example.com
- uses: legendify-dev/izri/apps/github-action@v1
  with:
    api-token: ${{ secrets.IZRI_API_TOKEN }}
    api-url: https://izri.internal.example.com

Database migrations

Schema changes ship as Drizzle migrations. After pulling a new version:

pnpm db:migrate

Migrations are immutable once released — never hand-edit one.

What self-hosting does not include

The visual signal's semantic diff is a paid capability. Self-hosted instances get the three deterministic signals plus visual capture and pixel diff. See plans and quotas.

Reading this with an agent? /docs/install/self-hosting.md serves the raw markdown.

Edit this page on GitHub →All docs