Skip to content

Get started

Everything you need to run a self-hosted Assemblix instance. We start with the dependencies, then walk through the example Docker Compose files we ship so you can pick the one that matches your setup.

Dependencies

For the Docker path (recommended) the only thing you install on the host is:

  • Docker with Compose v2 (docker compose).
  • openssl or python3 — only to generate the two required secrets (below). No full host Python is needed.

Everything else is pinned inside the images — you do not install these on the host:

Runtime Version Where
PostgreSQL 16 postgres:16-alpine
Python 3.13 API image (uv)
Node 22 web build
Redis 7 redis:7-alpine (optional queue tier)

Running natively, without Docker? You additionally need Python 3.13 + uv and Node 22 + yarn. See Running natively below.

1. Clone and configure

All configuration lives in a single .env at the repo root — the backend, the frontend build, and Docker Compose all read it.

git clone https://github.com/nmamizerov/assemblix.git
cd assemblix
cp .env.example .env

2. Set the two required secrets

The backend fails fast on startup without a valid JWT_SECRET_KEY (min 32 chars) and ENCRYPTION_KEY (a Fernet key). Generate them and paste the values into .env:

# JWT_SECRET_KEY
python3 -c "import secrets; print(secrets.token_urlsafe(48))"

# ENCRYPTION_KEY
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Only VITE_-prefixed vars are bundled into the frontend, so these secrets never reach the browser.

In a hurry? cp .env.example.quick .env gives you a file with only those two secrets to fill; everything else uses a working default. Or run make setup (./setup.sh) to generate the secrets, write .env, and bring the stack up in one command.

3. Pick an example Compose file

We ship three ready-made Compose files at the repo root — run all of them from the repo root. Pick the one that matches what you're doing.

docker-compose.yml — self-host production (default)

The lean self-host stack: postgres + migrate + api + web + docs. Migrations run automatically (a one-shot migrate service applies Alembic, then the API waits for it — so migrations never race across replicas). No Redis or worker.

docker compose up -d --build      # or: make prod
Service URL / detail
web http://localhost:8080
api http://localhost:8000
docs served under /docs (nginx proxies the MkDocs container)
db internal network only (not published)

Ports come from WEB_PORT (default 8080) and API_PORT (default 8000); the DB is reachable only over the internal Docker network via the postgres hostname.

docker-compose.dev.yml — development (live reload)

The full dev stack with hot reload — Postgres, Redis, the API (fastapi dev), the web app (Vite HMR), and the Arq worker. Source is bind-mounted; the API applies migrations on start.

docker compose -f docker-compose.dev.yml up --build      # or: make dev
Service URL / detail
web http://localhost:5173 (Vite HMR)
api http://localhost:8000 (fastapi dev reload)
postgres 127.0.0.1:5412 (localhost only)

Want just one service (e.g. only the DB, and run the apps another way)?

docker compose -f docker-compose.dev.yml up postgres

docker-compose.app.yml — app tier only (bring your own Postgres)

The web + api + docs tier with no bundled database — you point it at a managed Postgres (Neon, Supabase, RDS…). Designed for PaaS platforms like Coolify/Portainer, which surface the required vars in their UI. The API migrates on startup; only web is exposed publicly. Required: DATABASE_URL, JWT_SECRET_KEY, ENCRYPTION_KEY.

Optional: Redis + worker queue tier

Off by default. To enable distributed execution (the API enqueues, an Arq worker runs the graph), set in .env, then docker compose up -d:

COMPOSE_PROFILES=queue
REDIS_URL=redis://redis:6379/0
EXECUTION_QUEUE_ENABLED=true

Scale workers with WORKER_REPLICAS (each runs up to WORKER_MAX_JOBS concurrent jobs). For heavier deployments add the scale tier (COMPOSE_PROFILES=queue,scale), which puts a transaction-mode PgBouncer in front of Postgres so real DB connections are capped instead of growing with replicas × pool_size:

COMPOSE_PROFILES=queue,scale
DB_TARGET_HOST=pgbouncer          # route api/worker through the pooler
DB_TARGET_PORT=6432               # PgBouncer port, NOT the host-published POSTGRES_PORT
DB_DISABLE_STATEMENT_CACHE=true   # asyncpg-safe behind transaction pooling

Migrations always connect to Postgres directly — DDL is unsafe through a transaction-mode pooler.

make shortcuts

The root Makefile wraps the common commands:

make setup       # one-command bootstrap: generate secrets, write .env, start the stack
make dev         # docker-compose.dev.yml up (live reload)
make prod        # docker-compose.yml up -d --build
make down        # stop the dev stack
make logs        # follow dev logs

Essential configuration

The full .env has many knobs, but these are the ones that matter for a first self-host:

Variable Default Description
JWT_SECRET_KEY (required) JWT signing key, min 32 chars. Backend fails fast without it.
ENCRYPTION_KEY (required) Fernet key for encrypting stored credentials. Fails fast without it.
COMPOSE_PROFILES (empty) Empty = core only; queue = Redis + worker; queue,scale = + PgBouncer.
API_PORT / WEB_PORT 8000 / 8080 Host ports for the API and web app.
POSTGRES_PASSWORD assemblix Change for any non-local deployment.
DATABASE_URL …@localhost:5412/assemblix HOST value; compose overrides it to the postgres host. Set this yourself for docker-compose.app.yml.
CORS_ALLOWED_ORIGINS http://localhost:5173,… Comma-separated frontend origin(s), no trailing slash.
SYSTEM_OPENAI_API_KEY / SYSTEM_GEMINI_API_KEY / SYSTEM_DEEPSEEK_API_KEY (blank) System LLM keys; blank = self-host bring-your-own-key per credential.
SYSTEM_ELEVENLABS_API_KEY (blank) Voice output (TTS); optional.
HTTP_NODE_ALLOW_INTERNAL false SSRF opt-out for the HTTP-request node. Leave false unless you understand the risk.
BILLING_ENABLED false Enterprise billing; keep false for self-hosting.

Every variable is documented with comments in .env.example.

Running natively (without Docker)

The same root .env is read by host processes. Start just the database in Docker, then run each app on the host:

docker compose -f docker-compose.dev.yml up -d postgres

# Backend (from assemblix-app-api/) — reads ../.env, applies migrations, serves :8000
cd assemblix-app-api
uv sync
uv run alembic upgrade head
make dev

# Frontend (from assemblix-app-web/) — reads ../.env, serves :5173, proxies /api → :8000
cd assemblix-app-web
yarn install
yarn dev

Common issues

The app won't start (fail-fast on secrets). JWT_SECRET_KEY must be present and at least 32 characters; ENCRYPTION_KEY must be a valid Fernet key. Regenerate them (see step 2) and put them in the root .env.

Database connection errors. DATABASE_URL in .env is a host value; inside Compose it is overridden to the in-network postgres host, so you don't edit it for the standard flows. Running natively? Make sure Postgres is up and DATABASE_URL points at the host-published port (5412).

HTTP-request node is blocked. The HTTP node has SSRF protection and refuses internal / private addresses by default. Only set HTTP_NODE_ALLOW_INTERNAL=true if you understand the risk.

Queue features don't work. Distributed execution, Redis-backed debug events, and checkpointing require the queue tier. A blank REDIS_URL means single-process, Postgres-only operation — enable the queue profile (above).

/payments returns 404. Billing is an Enterprise feature, disabled by default (BILLING_ENABLED=false), so its routes return 404. This is expected for the source-available build.

Port conflicts. Change API_PORT, WEB_PORT, or POSTGRES_PORT in .env. The dev web port (5173) and worker metrics port (METRICS_PORT, default 9000) may also conflict.

Common operations.

docker compose ps                 # status
docker compose logs -f api        # follow API logs
docker compose down               # stop (keeps the DB volume)
docker compose down -v            # stop and delete the DB volume