Run Sergo
Configuration
Change how Sergo runs — the database, search, processors, ports and volumes.
Getting started runs Sergo with its defaults. This page covers what you can change and where each setting goes.
Where settings go
Section titled “Where settings go”.envnext tocompose.yaml. Compose reads it on every command. Use it for the variables listed on this page.compose.override.yamlnext tocompose.yaml, for anything else on this machine: another environment variable, an image, a memory limit, an extra service. Compose reads it automatically and merges it overcompose.yaml. Entries underenvironmentmerge by name, so you only write the ones you change. The repository ignores this file.- Admin › Settings in the console, for sign-up, session length, original files, retention and credits. Their environment variables only set the first value.
After you change .env or the override file, run docker compose up -d. Compose recreates the
containers whose settings changed.
Optional services
Section titled “Optional services”Two services start only when their profile is on.
| Profile | Service | What it is for |
|---|---|---|
embedder |
embedder |
A local embedding model, for search by meaning |
opensearch |
opensearch |
OpenSearch as a search backend |
Turn profiles on in .env, separated by commas:
COMPOSE_PROFILES=opensearch,embedderTo start one just once, pass it on the command line: docker compose --profile embedder up -d.
Search by meaning with a local model
Section titled “Search by meaning with a local model”- Turn on the
embedderprofile and rundocker compose up -d --build. The first start downloads the model, so it can take several minutes. - In Admin › Models, choose Connect a provider → TEI. Set the endpoint to
http://embedder:80, the address inside Docker (notlocalhost:8001). - Choose Add model → Embedding model and pick that provider. Sergo fills in the model name from the server and measures its dimensions. For e5 models it also fills in the query and passage prefixes.
- In Admin › Search index, create a New definition with that embedding model. When it has built, choose Use this one. Search keeps using the current definition until then.
| Variable | Default | |
|---|---|---|
SEARCH_EMBED_MODEL |
intfloat/multilingual-e5-small |
The Hugging Face model the embedder serves |
SEARCH_EMBED_MEM |
4g |
Memory limit. The small model uses about 2.5 GB; a base-size model needs 6g or more |
SEARCH_EMBED_MAX_BATCH |
128 |
The most inputs in one request |
Changing the model
Section titled “Changing the model”One embedder serves one model. After you change SEARCH_EMBED_MODEL, the embedding model you added
before stops working, and so does any definition that uses it. Sergo checks the model name each
time it adds vectors, and once per API process before it searches; until the API restarts, search
by meaning can return poor results instead of an error.
To switch without a gap, run the new model next to the old one. Add a service in
compose.override.yaml:
services: embedder-base: extends: file: compose.yaml service: embedder command: --model-id intfloat/multilingual-e5-base mem_limit: 6g ports: !override - "127.0.0.1:8002:80"Connect it as another TEI provider at http://embedder-base:80, add its embedding model, and build
a new definition with it. Once you use the new definition, remove the old service.
Your own Postgres
Section titled “Your own Postgres”To use a Postgres you already run, managed or not, put two lines in .env:
SERGO_DATABASE_URL=postgresql+psycopg://USER:PASSWORD@HOST:5432/sergoSERGO_POSTGRES_REPLICAS=0The API and the processors connect to that address, and the Postgres container no longer starts.
If it was running, the next docker compose up -d removes it. Its volume stays.
- Create the database first. The API creates and updates its tables when it starts, so the user needs permission to create tables.
- In the password, percent-encode
@,:and/(@becomes%40). Add?sslmode=requireto the end for TLS. - Inside a container,
localhostis the container itself. For Postgres on the same machine, usehost.docker.internalas the host. On Linux, also addextra_hosts: ["host.docker.internal:host-gateway"]toapi,baseline-cpu,baseline-ioandlibreofficeincompose.override.yaml, and let Postgres accept connections from the Docker network. - Search by meaning on Postgres needs the pgvector extension, version 0.8 or later. The API tries to
create it when it starts. If its user isn’t allowed to, run
CREATE EXTENSION vector;once as a superuser. Without pgvector, Sergo still runs and keyword search works, but a Postgres definition can’t use an embedding model. - Keep the connections Sergo opens under the server’s
max_connections. See Database connections.
To check the connection:
docker compose run --rm api python -m sergo.doctorOpenSearch
Section titled “OpenSearch”- Turn on the
opensearchprofile and rundocker compose up -d. Only the new container starts; the API keeps running. - In Admin › Search index, with no definition selected, find OpenSearch in the details and choose
Connect…. Enter
http://opensearch:9200. Sergo checks that the cluster answers before it saves the address, and uses it from the next search on. - Create a New definition with OpenSearch as its backend, and choose Use this one when it has built.
You can also set SEARCH_OPENSEARCH_URL in .env. It is only the first value: once you connect in
the console, the console’s address is used.
-
To use an OpenSearch cluster you already run, leave the profile off and connect to its address. If it needs a username and password, enter them in the same window; the password is encrypted and never shown again.
-
You can change the address later in the same place. If the definition in use has no index on the new cluster, Sergo refuses; switch to another definition first. Other OpenSearch definitions that aren’t on the new cluster need a Rebuild.
-
You can’t disconnect while a definition uses OpenSearch. Remove those definitions first.
-
SEARCH_OPENSEARCH_HEAPsets the Java heap (default512m). The container uses a little over 1 GB. -
The container runs without the security plugin, so its port listens on
127.0.0.1only. -
Text is analyzed as English. For Korean, use an image with the nori plugin, then create the definition through the API with
{"lexical": {"type": "nori"}}as itsindex_config(see Index settings). Set the image incompose.override.yaml:services:opensearch:image: sergo-opensearch-noribuild:dockerfile_inline: |FROM opensearchproject/opensearch:3.7.0RUN /usr/share/opensearch/bin/opensearch-plugin install --batch analysis-nori -
Other OpenSearch settings go under
opensearch.environmentincompose.override.yaml. The image reads settings with dots in their names, likediscovery.type, from environment variables.
Index settings
Section titled “Index settings”A definition also fixes how chunks are indexed: how words are cut and scored, and how vectors are compared. To change that, choose Change… under Index settings in a definition’s details, or expand Index settings in New definition. The change is built as a new definition next to the one in use, so search keeps answering; compare the two, then switch.
- OpenSearch: stemmer, stop words, BM25
k1andb, and synonyms — one rule per line, likelaptop, notebook(both ways) orlaptop => notebook(one way). Synonyms apply to queries. - Postgres: the language, which is a Postgres text search config (
simplekeeps every word as written), and HNSWmandef_constructionfor vectors. - Both: vector distance — cosine, inner product or Euclidean — when the definition has an embedding model.
Other keys can be set through the API: send index_config with POST /v1/admin/indexes. Only the
keys you send change; the rest come from the definition you start from, or from the backend’s
defaults when you pick another backend.
Processors
Section titled “Processors”Three services read files. Each takes jobs from a queue in Postgres, so you can run more copies of any of them.
| Service | What it does | Jobs at once, per copy |
|---|---|---|
baseline-cpu |
Reads PDF, Word and text files without AI | 2 |
baseline-io |
Sends pages to a model (Standard (AI)) | 16 |
libreoffice |
Converts office files to PDF | 2 |
The jobs at once are fixed in each image. Admin › Processors shows the copies that are running.
| Variable | Default | |
|---|---|---|
SERGO_BASELINE_CPU_REPLICAS |
1 |
Copies of baseline-cpu |
SERGO_BASELINE_IO_REPLICAS |
1 |
Copies of baseline-io |
SERGO_LIBREOFFICE_REPLICAS |
1 |
Copies of libreoffice |
SERGO_BASELINE_CPU_DB_POOL |
3 |
Database connections of each baseline-cpu copy |
SERGO_BASELINE_IO_DB_POOL |
4 |
Database connections of each baseline-io copy |
SERGO_LIBREOFFICE_DB_POOL |
3 |
Database connections of each libreoffice copy |
SERGO_LIBREOFFICE_MEM |
2g |
Memory limit of each libreoffice copy |
baseline-cpuuses one CPU core per copy, however many jobs it holds. To use more cores, run more copies.baseline-iospends most of its time waiting for the model, so 16 jobs per copy is usually enough. The limit for each model is its Jobs at once in Admin › Models.libreofficestarts a LibreOffice process for each job, and the memory limit applies to each copy, so two copies use twice as much. A conversion that runs longer than 300 seconds is stopped and tried again; to change that, setSERGO_LIBREOFFICE_TIMEOUTforlibreofficeincompose.override.yaml. If you don’t run this service, office files can’t be processed. PDF and text files still are.
When a processor stops, it has 60 seconds to finish the jobs it holds. A job it doesn’t finish goes back to the queue and another copy picks it up later.
Database connections
Section titled “Database connections”Every process opens its own connections to Postgres:
- The API opens up to 30: a pool of 20 and 10 more when busy. Change them with
SERGO_DB_POOLandSERGO_DB_POOL_OVERFLOWforapiincompose.override.yaml. - Each processor copy opens the number set by its
_DB_POOLvariable above.
With the defaults that is about 40, well under the 100 that Postgres allows by default. If you run more copies, or your Postgres allows fewer connections, add up copies × connections for each service plus the API’s 30.
Sergo sends three kinds of email: a link to reset a forgotten password, a link that confirms the
address someone signs up with, and a sign-in code. All need an SMTP server. Any email service that
offers SMTP works (Resend, Amazon SES, Postmark and others), and so does your company’s mail server.
Without SERGO_SMTP_HOST, none of these exist, everyone signs in with a password, and the sign-in
page has no Forgot password? link.
| Variable | Default | |
|---|---|---|
SERGO_SMTP_HOST |
none | The SMTP server. Leave it out to send no email |
SERGO_SMTP_PORT |
587 |
|
SERGO_SMTP_SECURITY |
starttls |
starttls for port 587, tls for port 465, or none for a relay on your own network |
SERGO_SMTP_USERNAME |
none | Leave both out if the server doesn’t ask you to sign in |
SERGO_SMTP_PASSWORD |
none | |
SERGO_MAIL_FROM |
none | The sender, like "Sergo <no-reply@sergo.example.com>". Required with SERGO_SMTP_HOST |
SERGO_PUBLIC_URL |
none | The console’s address, which the links in the email point to, like https://sergo.example.com/. Required with SERGO_SMTP_HOST |
SERGO_VERIFY_EMAIL |
true |
Whether people confirm their email at sign-up |
SERGO_SIGN_IN |
password |
How people sign in: password, or email for a code sent to their email |
With email set up:
- Forgot password? works for people whose username is an email address. The link lasts an hour and signs them out everywhere once they choose a new password. Others ask an admin to set a new one.
- Sign-up asks for an email address and sends a link to it. The account is created when they open the link, which lasts 24 hours. With sign-up set to approval, the account then waits for an admin. Turn this off with Confirm email at sign-up in Admin › Settings.
- Email codes (Sign in with › Email code in Admin › Settings) replace passwords. People enter their email and get a 6-digit code and a link, both good once for 10 minutes. The same steps sign in an existing account or create a new one when sign-up allows it, so there’s no separate sign-up form. Anyone can still set a password in Account, and Use a password instead on the sign-in page takes it. Five wrong codes end a code.
Before you send for real, verify your sending domain with your email service. It gives you SPF and DKIM records to add to your DNS, and without them your email is likely to land in spam. Many cloud providers block outgoing port 25, so use the service’s port 587 or 465.
Sign in with GitHub or Google
Section titled “Sign in with GitHub or Google”Add a Continue with GitHub or Continue with Google button to the sign-in page by creating an OAuth app
with that provider and putting its client ID and secret in .env. Both need SERGO_PUBLIC_URL. The
address to give the provider as its callback, or redirect URI, is your SERGO_PUBLIC_URL followed by
v1/auth/oauth/github/callback or v1/auth/oauth/google/callback.
| Variable | Default | |
|---|---|---|
SERGO_OAUTH_GITHUB_CLIENT_ID |
none | From GitHub: Settings › Developer settings › OAuth Apps |
SERGO_OAUTH_GITHUB_CLIENT_SECRET |
none | |
SERGO_OAUTH_GOOGLE_CLIENT_ID |
none | From Google Cloud: Google Auth Platform › Clients, as a Web application |
SERGO_OAUTH_GOOGLE_CLIENT_SECRET |
none |
Sergo uses only the verified email address and name the provider gives it. A person who signs in with an email address that already has an account here gets that account. Otherwise sign-up decides: an open install creates the account, an install that approves sign-ups adds them to the waiting list, and an install with sign-up off turns them away.
API settings
Section titled “API settings”| Variable | Default | |
|---|---|---|
SERGO_BOOTSTRAP_ADMIN_PASSWORD |
none | The first password of admin, used only when the API creates that account on its first start. Left out, the API makes one up and prints it once in its log. Either way, admin sets a new one at the first sign-in |
SERGO_SIGNUP |
off |
Who can sign up: off, approval (an admin approves) or open |
SERGO_REQUIRE_NAME |
true |
Whether new accounts must give a name. With false the name is optional, and an account without one goes by its username |
SERGO_PEOPLE_SEARCH |
directory |
How people find someone to share with: directory searches names and usernames, exact finds someone only from their whole username or email. Use exact when strangers can sign up, so they can’t list everyone. Admins always search names |
SERGO_TERMS_URL |
none | Your terms of service. The sign-up page links it in a line under the button |
SERGO_PRIVACY_URL |
none | Your privacy policy, linked in the same line. With neither set, the line doesn’t show |
SERGO_DOCS_URL |
none | Where the console’s Docs menu and help links point, like https://sergo.example.com/docs/. Leave it out to hide them, for example on a network that can’t reach this website |
SERGO_ORIGINAL_ON_DEMAND |
false |
When true, a document’s original file loads only after you choose Load original |
SERGO_MAX_FILE_MB |
100 |
The largest file anyone can upload, in MB (up to 1000) |
SERGO_USER_STORAGE_MB |
1024 |
How much each person’s uploads can add up to, in MB, trash included. 0 means no limit. Admins have none |
SERGO_RATE_LOGIN |
10/15m |
Failed sign-ins allowed per account and address. Requests over a limit get 429 with Retry-After. 0 turns a limit off |
SERGO_RATE_SIGNUP |
20/1h |
Sign-ups per address |
SERGO_RATE_KEY_IP |
300/1m |
Requests per API key from one address, over the HTTP API and MCP |
SERGO_RATE_KEY |
0 |
Requests per API key from everywhere together — for a key many people share |
SERGO_RATE_SEARCH |
300/1m |
Searches per person in the console. Admins have no limit |
SERGO_RATE_UPLOAD |
600/1m |
Uploads per person in the console. Admins have no limit |
SERGO_RATE_PEOPLE |
300/1m |
Searches for people to share with, per person. Admins have no limit |
SERGO_RATE_MAIL |
5/1h |
Emails to one address, all kinds together |
SERGO_RATE_MAIL_IP |
20/1h |
Password reset and sign-in code requests per address |
SERGO_RATE_OAUTH |
30/1m |
GitHub and Google sign-ins started per address |
SERGO_TRUSTED_PROXIES |
private ranges | Proxies whose X-Forwarded-For the API believes when it looks for the client’s address |
SERGO_SIGNUP, SERGO_REQUIRE_NAME, SERGO_PEOPLE_SEARCH, SERGO_VERIFY_EMAIL, SERGO_SIGN_IN, SERGO_ORIGINAL_ON_DEMAND and the two upload limits only set the first value. After that, change them in
Admin › Settings, along with session length, trash and audit retention, and the credit unit and
margin.
Ports and volumes
Section titled “Ports and volumes”Every port listens on 127.0.0.1 only.
| Port | Service | |
|---|---|---|
| 3000 | console |
The console, the HTTP API under /v1 and MCP at /mcp. Put your reverse proxy in front of this one |
| 8000 | api |
The API itself, for tools on this machine |
| 5432 | postgres |
|
| 8001 | embedder |
Only with the embedder profile |
| 9200 | opensearch |
Only with the opensearch profile |
| Volume | Holds |
|---|---|
pgdata |
The database |
originals |
Uploaded files |
secrets |
The master key that seals model provider API keys. Back it up with the database; without it, the keys have to be entered again |
models |
Downloaded embedding models |
osdata |
OpenSearch indexes. Rebuild in Admin › Search index fills them again from Postgres |