Skip to main content
This guide walks you through a production-ready deployment using the exulu-example repository. Complete the Requirements checklist before you begin.
1
Clone the example repository
2
git clone git@github.com:Qventu/exulu-example.git
cd exulu-example
3
Create your environment file
4
cp .env.example .env
5
Open .env and fill in the critical variables listed below. The file ships with comments that explain each one.
6
Authentication and security
7
VariableWhat to setNEXTAUTH_SECRETA random string (at least 32 chars). Generate one with openssl rand -base64 32.LITELLM_MASTER_KEYThe master key the backend and workers use to authenticate against the LiteLLM proxy. Choose a strong random value.
8
INTERNAL_SECRET is optional and is not present in the .env.example template. Set it only when you have internal service-to-service calls that use the internal request header; IMP does not require it for standard deployments.
9
npm registry access
10
@exulu/backend is published to the public npm registry — no token is required. The NPM_TOKEN build argument in the example repo’s Dockerfiles is legacy plumbing and may be left empty or unset.
11
Postgres
12
VariableWhat to setPOSTGRES_DB_HOSTHostname or IP of your Postgres server (e.g., exulu-pgvector when using the services compose file).POSTGRES_DB_PORT5432POSTGRES_DB_USERpostgresPOSTGRES_DB_PASSWORDYour passwordPOSTGRES_DB_NAMEDatabase name for IMP (e.g., exulu)LITELLM_DATABASE_URLConnection string for the separate LiteLLM database — must be a different database than POSTGRES_DB_NAME.
13
S3-compatible storage
14
VariableWhat to setCOMPANION_S3_REGIONYour bucket region (e.g., eu-central-1)COMPANION_S3_KEYAccess key IDCOMPANION_S3_SECRETSecret access keyCOMPANION_S3_BUCKETBucket name (must already exist)COMPANION_S3_ENDPOINTEndpoint URL (set to http://exulu-minio:9000 when using the MinIO container)
15
Frontend and backend URLs
16
VariableWhat to setFRONTENDThe public URL users open in their browser (e.g., https://imp.example.com)BACKENDThe public URL of the backend API — used client-side (e.g., https://api.imp.example.com)NEXT_BACKENDThe backend URL reachable from the frontend container’s network. If the frontend container and backend container are on the same Docker network, use the container name: http://exulu-backend:9001.NEXTAUTH_URLSame as FRONTEND.
17
For the LiteLLM proxy to work you also need to configure config.litellm.yaml with at least one model entry before starting the backend. Copy config.litellm.yaml from the repo and edit the model_list section to add your providers.
18
Start the infrastructure services
19
Launch Postgres (with pgvector), Redis, and MinIO:
20
docker compose -f docker-compose.services.yml up -d
21
This starts three containers: exulu-pgvector, exulu-redis, and exulu-minio. Wait for the health checks to pass before proceeding.
22
The services compose file mounts ./postgres/schema.sql into the pgvector container’s init directory. This file is referenced in the compose definition but may not exist in all repo checkouts. If Postgres fails to start, check whether postgres/schema.sql is present and create it as an empty file if needed (mkdir -p postgres && touch postgres/schema.sql).
23
Build and start the backend
24
The backend has no published Docker image. Build it locally from your exulu-example checkout so your own configuration is baked in. @exulu/backend installs from the public npm registry — no token is needed.
25
docker compose -f docker-compose.backend.yml up -d --build
26
This builds the image and starts the exulu-backend container on ports 9001 and 4000.
27
Capture the first-boot log output before it scrolls away. On the very first startup, initdb runs automatically and prints a one-time admin password and API key to stdout. You cannot retrieve the API key again after this point.
The default admin credentials are admin@exulu.com / admin. Change the password immediately after first login. The API key printed to the log is the only copy — store it in your secrets manager now.initdb runs on every backend boot and logs a freshly generated key each time, but only the key from the very first startup is ever persisted. Keys logged on subsequent startups are never stored and will not work.
28
The security_opt flags (seccomp:unconfined, apparmor:unconfined) in the compose file are required for the skill sandbox (bwrap) to create user namespaces. Without them, sandboxed skill execution will fail with permission errors.
29
Start the worker (optional)
30
Workers handle background jobs: knowledge ingestion, embeddings, evals, and routines. Skip this step if you do not need these features.
31
docker compose -f docker-compose.worker.yml up -d --build
32
The worker container starts on port 9002. It connects to the LiteLLM proxy on port 4000 (the backend must be running first). Give the worker container at least 8 GB of memory — knowledge processing tasks can hold large document representations in memory. The Docker entrypoint does not set NODE_OPTIONS by default; set it yourself via the compose environment section:
33
environment:
  NODE_OPTIONS: "--max-old-space-size=8192"
34
docker-compose.worker.yml does not include an env_file entry (unlike the backend compose file). Outside Dokploy — where environment variables are injected by the platform — the worker will start without database and Redis credentials and crash on first use. Add the following to docker-compose.worker.yml under the worker service, or inject the variables another way:
35
Start the frontend
36
docker compose -f docker-compose.frontend.yml up -d
37
This pulls ghcr.io/qventu/exulu-frontend:latest and starts the container on port 3000. No build step is needed.
38
Verify the deployment
39
Check that all containers are running:
40
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
41
You should see exulu-pgvector, exulu-redis, exulu-minio, exulu-backend, exulu-worker (if started), and exulu-frontend all in Up status.
42
Open your FRONTEND URL in a browser and log in with admin@exulu.com and the password from the first-boot log. Navigate to Administration → Models to verify that the LiteLLM proxy is reachable and your configured models appear.

Production network note

The backend and worker compose files attach to dokploy-network, an external Docker network used by Dokploy. If you are not using Dokploy, create a shared network and update the networks section in both compose files to match your setup before running the commands above.

Next steps

See the service guides for customizing config.litellm.yaml, SMTP and auth, and observability, and the environment variable reference for the full configuration surface. See LiteLLM service for adding and managing model providers through the proxy.