Skip to content

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.

  • .env next to compose.yaml. Compose reads it on every command. Use it for the variables listed on this page.
  • compose.override.yaml next to compose.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 over compose.yaml. Entries under environment merge 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.

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,embedder

To start one just once, pass it on the command line: docker compose --profile embedder up -d.

  1. Turn on the embedder profile and run docker compose up -d --build. The first start downloads the model, so it can take several minutes.
  2. In Admin › Models, choose Connect a provider → TEI. Set the endpoint to http://embedder:80, the address inside Docker (not localhost:8001).
  3. 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.
  4. 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

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.

To use a Postgres you already run, managed or not, put two lines in .env:

SERGO_DATABASE_URL=postgresql+psycopg://USER:PASSWORD@HOST:5432/sergo
SERGO_POSTGRES_REPLICAS=0

The 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=require to the end for TLS.
  • Inside a container, localhost is the container itself. For Postgres on the same machine, use host.docker.internal as the host. On Linux, also add extra_hosts: ["host.docker.internal:host-gateway"] to api, baseline-cpu, baseline-io and libreoffice in compose.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:

Terminal window
docker compose run --rm api python -m sergo.doctor
  1. Turn on the opensearch profile and run docker compose up -d. Only the new container starts; the API keeps running.
  2. 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.
  3. 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_HEAP sets the Java heap (default 512m). The container uses a little over 1 GB.

  • The container runs without the security plugin, so its port listens on 127.0.0.1 only.

  • 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 its index_config (see Index settings). Set the image in compose.override.yaml:

    services:
    opensearch:
    image: sergo-opensearch-nori
    build:
    dockerfile_inline: |
    FROM opensearchproject/opensearch:3.7.0
    RUN /usr/share/opensearch/bin/opensearch-plugin install --batch analysis-nori
  • Other OpenSearch settings go under opensearch.environment in compose.override.yaml. The image reads settings with dots in their names, like discovery.type, from environment variables.

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 k1 and b, and synonyms — one rule per line, like laptop, notebook (both ways) or laptop => notebook (one way). Synonyms apply to queries.
  • Postgres: the language, which is a Postgres text search config (simple keeps every word as written), and HNSW m and ef_construction for 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.

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-cpu uses one CPU core per copy, however many jobs it holds. To use more cores, run more copies.
  • baseline-io spends 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.
  • libreoffice starts 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, set SERGO_LIBREOFFICE_TIMEOUT for libreoffice in compose.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.

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_POOL and SERGO_DB_POOL_OVERFLOW for api in compose.override.yaml.
  • Each processor copy opens the number set by its _DB_POOL variable 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.

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.

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.

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