Quickstart · Self-hosted
From nothing to a working search, in about fifteen minutes.
You need Docker with Compose, about 6 GB of free disk, and 3 GB of spare RAM.
Create a working directory
mkdir lenshub && cd lenshubWrite .env
Generate real values — do not copy the examples.
echo "POSTGRES_DB=lenshub" > .env
echo "POSTGRES_USER=lenshub" >> .env
echo "POSTGRES_PASSWORD=$(openssl rand -hex 16)" >> .env
echo "JWT_SECRET=$(openssl rand -hex 32)" >> .env
echo "ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .env
echo "LENSHUB_REGISTRY=<the registry path you were given>" >> .envLENSHUB_REGISTRY is the container registry your images are pulled from. It
comes with your access, along with the credentials to authenticate against it —
see Getting access if you do not have it yet.
Back up ENCRYPTION_KEY before you go further. It encrypts your connector
credentials. If you lose it, every stored credential becomes unreadable and
has to be re-entered. If you change it, the same thing happens.
Write docker-compose.yml
services:
postgres:
image: ${LENSHUB_REGISTRY}/postgres:latest
restart: unless-stopped
volumes: [pgdata:/var/lib/postgresql/data]
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 5s
retries: 5
api:
image: ${LENSHUB_REGISTRY}/api:latest
restart: unless-stopped
ports: ["127.0.0.1:8080:8080"]
depends_on:
postgres: { condition: service_healthy }
volumes: [uploads:/data/uploads, attachments:/data/attachments]
environment:
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable
JWT_SECRET: ${JWT_SECRET}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
LLM_EMBED_BASE_URL: http://embedding:8001/v1
BASE_URL: http://localhost:3000
CORS_ORIGIN: http://localhost:3000
web:
image: ${LENSHUB_REGISTRY}/web:latest
restart: unless-stopped
ports: ["127.0.0.1:3000:3000"]
depends_on: [api]
environment:
NEXT_PUBLIC_API_URL: http://localhost:8080
API_INTERNAL_URL: http://api:8080
worker:
image: ${LENSHUB_REGISTRY}/worker:latest
restart: unless-stopped
depends_on:
postgres: { condition: service_healthy }
volumes: [git-clones:/tmp/lenshub-git-clones, attachments:/data/attachments]
environment:
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
LLM_EMBED_BASE_URL: http://embedding:8001/v1
embedding:
image: ${LENSHUB_REGISTRY}/embedding:latest
restart: unless-stopped
volumes: [embedding-models:/data]
volumes:
pgdata:
uploads:
attachments:
git-clones:
embedding-models:Start it
docker login <your registry host> # with the credentials you were given
docker compose pull
docker compose up -dThe first pull is around 4 GB. Watch for everything to become healthy:
docker compose psCreate your first user
The database starts empty. This creates an administrator and prints an API key.
docker compose run --rm \
-e SEED_ADMIN_EMAIL=you@yourcompany.com \
-e SEED_ADMIN_PASSWORD='pick-a-strong-one' \
api /app/lenshub seedSave the API key it prints. It is shown once. You can always create more later in Settings.
The command refuses to run a second time once an administrator exists, so it is safe to leave in your notes.
Sign in
Open http://localhost:3000 and log in with the email and password you just used.
Next
- Connect an AI provider — needed for chat and automatic tagging. Search works without it.
- Add your first connector — start with a git repository; it is the fastest to see working.
- Connect your first agent.
This setup binds everything to 127.0.0.1 and runs without HTTPS, which is
right for trying it out on one machine. Before anyone else uses it, read
Putting it on a network.