Skip to content
Omicron

Self-hosting

Upgrading

Two commands, automatic migrations, and the guarantee that upgrades never touch your data.

git pull
docker compose up -d --build
docker image prune -f
docker builder prune -f      # or the equivalent podman commands

If you installed with the one-liner, re-running it does the same thing — including the cache cleanup below.

What happens

  • Migrations run automatically on backend startup and are idempotent — a no-op when the schema is already current.
  • A rebuild recreates the containers but never the volumes, so your data, secrets, and TLS certificates are preserved.
  • image prune/builder prune drop the previous build’s now-untagged image layers and build cache, so a --build upgrade never leaves disk usage creeping up. Both only remove untagged, unused layers — never a named volume — so this is safe to run every time.
  • The app version is logged on boot and exposed at GET /version.

Migration policy

Upgrades must never break a running instance, so schema changes are additive only within a version:

  1. Add new columns and tables (nullable or with defaults) and migrate data.
  2. Ship code that works with both the old and the new shape.
  3. Remove old columns only in a later major release.

Migrations are versioned SQL in apps/backend/drizzle/, replayed at runtime by src/db/migrate.ts. drizzle-kit is not required inside the container.

One-time step: the AI-scraper-shield release

That release changed the bundled Caddyfile to open an internal admin API the scraper-shield toggle uses. Caddy only reads that file at startup, and a plain up -d does not recreate an unchanged service — so recreate the containers once:

docker compose up -d --build --force-recreate

This recreates containers, never volumes, so all data and certificates survive. If you skip it nothing breaks: the instance runs normally and protection stays off (the default); you simply cannot switch it on in Admin → Security until Caddy has been recreated. After that release, normal up -d upgrades resume.

One-time step: the Redis 8 release

That release moved the bundled Redis from 7.4 to 8. Redis 7.4 is offered only under RSALv2 and SSPLv1, neither of which is an open-source licence; Redis 8 restores AGPLv3 as an option, matching Omicron’s own licence.

Nothing is required of you. Redis 8 reads the files 7.4 wrote, so a normal git pull && docker compose up -d --build upgrades it in place. The backend logs a few seconds of ENOTFOUND redis while the container is replaced, then reconnects on its own.

One-time step: the scraper-shield fixes release

That release changed the bundled Caddyfile again, so it needs the same recreate as the original shield release:

git pull && docker compose up -d --build --force-recreate

A plain up -d leaves Caddy on the old file and the fixes silently do not apply. What you get once it is recreated:

  • The challenge page no longer hangs on “Calculating…” until the reader refreshes.
  • Page loads no longer fail with a 500 when the reader’s IP arrives from a private network.
  • The challenge signing key is generated once into the secrets volume, so restarts and upgrades stop re-challenging readers who already solved one.

Containers are recreated, never volumes, so data and certificates survive. If the shield is on, it drops for a few seconds while Caddy restarts and the backend re-applies it — readers see the site unprotected, not broken.

The release also adds ANUBIS_REDIRECT_DOMAINS. Nothing breaks if you leave it unset, but a public instance should set it — see Environment variables.

One-time step: the challenge status-code release

The release that made a challenge answer 503 instead of 200 touches two files the containers only read at startup — botPolicy.yaml and the Caddyfile — so it needs the same recreate:

git pull && docker compose up -d --build --force-recreate

A plain up -d leaves the challenge answering 200 with no Retry-After. Nothing breaks either way; the point of the change is that automated clients — search engines, uptime monitors, link checkers — stop being told a refusal succeeded. See what a challenged request gets back.

If you only edited botPolicy.yaml yourself and want it picked up without recreating anything else, docker compose restart anubis is enough.

Rolling back

Check out the previous tag and rebuild:

git checkout <previous-tag>
docker compose up -d --build

Never run this on a live instance

docker compose down -v   # deletes every volume: database, uploads, secrets, certs

down (without -v) is safe — it stops containers and leaves your data alone.

Checking what you are running

curl localhost:8000/version
docker compose ps
docker compose logs --since 5m backend

Found a mistake?Edit this page on GitHub.