Skip to content

Self-hosting ​

openlocale ships as one Node process that serves the dashboard, the REST API, and the delivery endpoints. There is no separate worker, queue, or cache to operate.

sh
docker run -d --name openlocale \
  -p 3000:3000 \
  -v openlocale-data:/data \
  -e OPENLOCALE_AUTH_SECRET=$(openssl rand -hex 32) \
  -e OPENLOCALE_BASE_URL=https://locale.example.com \
  openlocale/openlocale:latest

The image defaults SQLite to /data/openlocale.db, so the mounted volume is the only persistence requirement. Database migrations run automatically on boot. That's the whole deployment. (Building your own image is just docker build -t openlocale . in the repo.)

Running Kubernetes? See Deploying on Kubernetes.

Set the secret and base URL

OPENLOCALE_AUTH_SECRET signs sessions — the server refuses to start in production without it. OPENLOCALE_BASE_URL must match the public URL users reach the instance at, or OIDC callbacks and cookies will misbehave.

SQLite or Postgres? ​

SQLite (default)Postgres
Setupzero — one filerun a server
Fitsmost teams, single instancehigh write volume, HA
Live eventsin-processLISTEN/NOTIFY across instances
Backupscopy the filepg_dump / your usual tooling

Switch by pointing OPENLOCALE_DB_URL at Postgres:

sh
OPENLOCALE_DB_URL=postgres://openlocale:secret@db:5432/openlocale

The repo's docker-compose.yml includes a ready-to-use Postgres service. When multiple app instances share one Postgres, live translation events fan out to all of them automatically via LISTEN/NOTIFY — SSE clients can connect to any instance.

Reverse proxies and SSE ​

The live event stream is standard text/event-stream. Two rules for whatever sits in front:

  1. Don't buffer it. nginx example:

    nginx
    location /api/v1/cdn/ {
      proxy_pass http://openlocale:3000;
      proxy_buffering off;
      proxy_read_timeout 1h;
    }
  2. Allow long-lived connections. The server sends a heartbeat comment every 25 seconds to keep intermediaries from timing out idle streams.

If a proxy strips SSE anyway, clients using @openlocale/sdk degrade gracefully to manifest polling — live updates still arrive, just slower.

Caching the delivery endpoints ​

Bundle responses carry ETag and Cache-Control: public, max-age=0, must-revalidate, so any CDN (Cloudflare, Fastly, CloudFront) can sit in front of /api/v1/cdn/* and serve 304s close to your users. Version numbers in the ETag change on every write, so caches never serve stale content.

A per-IP rate limit protects the public endpoints as a backstop (OPENLOCALE_CDN_RATE_LIMIT, default 300 req/min, 0 disables). Do real rate limiting at the edge.

Environment variables ​

See the full environment variable reference.

Upgrades and backups ​

  • Upgrade: pull the new image and restart — migrations apply on boot.
  • Back up: the database is the only state. SQLite: snapshot the file (it's WAL-mode; use sqlite3 …/openlocale.db ".backup …" for a consistent copy). Postgres: your normal backup tooling.
  • The license key, SSO connectors, and all settings live in the database.

MIT licensed core. AI features unlock with a license key.