Semestra

Images

The default image or the smaller one, a fixed version, or an image you build yourself.

Both images are on the GitHub Container Registry as ghcr.io/jinyuanzhou-leo/semestra-allinone and are public.

ImageTagsPlatformsRAMSelect with .env
With local OCR and embedding (default)latest-local, 1.2.0-localamd64, arm64~4 GBnothing
Without themlatest, 1.2.0amd64~1 GBSEMESTRA_IMAGE_VARIANT= (empty)

The default image used the suffix -ocr before; -ocr tags keep working as an alias until 2.0.

On an arm64 machine, such as a Raspberry Pi or Docker on Apple Silicon, use the default image.

Local OCR

The default image reads scanned PDF pages and photos on the server with PaddleOCR (CPU only), so no AI model is called for them. Reading takes up to 2 GB of RAM on top of the app's 1 GB, one document at a time, about 2 seconds per page.

Each user picks the model in the OCR row of AI settings → Models: a local PaddleOCR model or a provider's model. The picker shows each local model's download size, speed and accuracy. Local models download into the data volume the first time they are needed, or when the user clicks Download in that row, and the app shows the progress. A server that will lose internet access needs them fetched while it still has it:

docker compose exec semestra semestra ocr-models --all

With the smaller image, users choose a provider's model for OCR and Search. Switch either way by changing SEMESTRA_IMAGE_VARIANT and running docker compose up -d; no data changes.

Local embedding

The default image also turns text into vectors for search on the server, so semantic search in files works without an AI provider. It runs ONNX models from Hugging Face on ONNX Runtime, CPU only. When a user has a provider (or the server has an instance-wide provider) with an embedding model, Auto keeps using that; otherwise it uses the local default. Users can also pick a model in the Search row of AI settings → Models:

ModelNotes
multilingual-e5-smallDefault. Smallest and fastest: about 27 passages a second on an Apple M3, about 1 GB of RAM while in use
bge-m3Balanced, good in Chinese and English: about 3 passages a second on an Apple M3, about 1.6 GB of RAM
qwen3-embedding-0.6bMost accurate, much slower: about 0.7 passages a second on an Apple M3, up to about 3.3 GB of RAM

Users can also type the name of any Hugging Face model that has an ONNX version (an onnx/ folder and a tokenizer.json). Models download into the data volume the first time they are needed, like OCR models; files stay searchable by keyword until then, and their vectors are added once the model is here. Changing models rebuilds the vectors in the background. To fetch models ahead of time:

docker compose exec semestra semestra embedding-models --all

The model stays loaded in its own process and exits after ten idle minutes. Docker cannot use a Mac's GPU, so this runs on the CPU. For GPU speed, run Ollama on the host and pick its embedding model instead. Settings are under All settings.

Free up disk space

Settings → Data → Local models shows how much space each downloaded model takes and deletes the ones nobody needs. Any account can do this; a model someone still uses downloads again when it is next needed.

Stay on one version

latest follows every release. To upgrade only when you choose, set the version in .env:

.env
SEMESTRA_VERSION=1.2.0

Pre-releases (for example 2.0.0-beta.0, listed as pre-releases on GitHub) never become latest. To try one, set its version the same way; set it back to latest to return to stable releases. Back up first: a newer version upgrades the database, and an older version may not be able to open it.

Build the image yourself

From the repository root:

docker build -f deploy/docker/Dockerfile --target allinone-local \
  -t ghcr.io/jinyuanzhou-leo/semestra-allinone:dev-local .

Use --target allinone and the tag :dev for the smaller image. Then point the compose file at it:

deploy/selfhost/.env
SEMESTRA_VERSION=dev

To upgrade, git pull, build again and run docker compose up -d (not docker compose pull).

Build arguments for the web app, baked into the image:

ArgumentEffect
VITE_EMAIL_DOMAINSDomains suggested after @ on the sign-in page, e.g. school.edu,gmail.com
VITE_SENTRY_DSNReport errors in the browser to your Sentry project