Skip to content
Documentation

Ship translations with confidence

Everything you need to install, configure, deploy and maintain i18n Dashboard โ€” whether you're a solo developer or an enterprise team.

Start here

Quickstart with Docker

The official image arnaudprioul/i18n-dashboard ships on Docker Hub for both linux/amd64 and linux/arm64. Three ways to run it depending on your team size.

Local modeSQLite, no auth

Single user, single team. Translations live in a Docker volume. Best for self-hosting up to ~100k keys. Save this as docker-compose.yml and run docker compose up -d:

Full quickstart, env vars and troubleshooting on the Docker Hub overview.

docker-compose.yml

Server modePostgres + central auth

Multi-team, JWT-verified via JWKS. The webapp validates tokens offline against your auth server's /api/auth/.well-known/jwks.json.

.env

Desktop modeTauri, local data

Native macOS / Windows / Linux build with embedded Nitro server and SQLite. No Docker, no database to configure. Signing in goes through your account online.

  • macOS
  • Windows
  • Linux

Download the desktop app โ†’

Browse

Find it by what you're doing

Eight groups: getting started, framework guides, deployment, administration, quality, reference, billing and support.

Configuration

Environment variables

Every runtimeConfig value is overridable at runtime via the NUXT_* prefix โ€” Nitro's canonical convention. Bare names like I18N_DB_HOST are silently ignored unless explicitly listed as wire-protocol vars.

The full reference (every variable, default, scope, and migration note) lives on the Docker Hub page where it stays close to the image:

Open the full env var reference on Docker Hub โ†’

Most common knobs

  • I18N_APP_MODE โ€” local / server / desktop. Server mode requires a real DB and a central auth.
  • NUXT_DB_CLIENT + NUXT_DB_HOST + NUXT_DB_NAME + NUXT_DB_USER + NUXT_DB_PASSWORD โ€” Postgres / SQLite / MySQL connection.
  • NUXT_SESSION_SECRET โ€” required in production. Generate with openssl rand -hex 32.
  • NUXT_CENTRAL_AUTH_URL + NUXT_PUBLIC_CENTRAL_AUTH_URL โ€” server-side and browser-side URLs of your auth issuer (must be JWKS-compatible).
  • NUXT_AUTH_JWT_ISSUER + NUXT_AUTH_JWT_AUDIENCE โ€” expected iss / aud claims on access tokens.
  • NUXT_INTERNAL_API_SECRET โ€” shared HMAC for webapp โ†” central-auth side calls. Must be identical on both sides.
  • NUXT_SMTP_HOST + NUXT_SMTP_PORT + NUXT_SMTP_USER + NUXT_SMTP_PASS + NUXT_SMTP_FROM โ€” workflow notifications. Leave host empty to disable email.
  • NUXT_DASHBOARD_URL โ€” the public URL of this webapp instance, used in transactional email links.

SMTP setup in 30 seconds

Pick any SMTP relay and pass the four values below โ€” the image will deliver email through it on every workflow event.

.env

For local dev / smoke testing, point to the bundled Mailpit service: NUXT_SMTP_HOST=mailpit, NUXT_SMTP_PORT=1025.

Roles & permissions

A role is granted per project, not instance-wide โ€” three tiers, each unlocking more of what that project allows.

translatormoderatoradminsuper_admin
Edit translationsYesYesYesYes
Approve translationsNoYesYesYes
Manage project (settings, scan, sync)NoNoYesYes
Manage project membersNoNoYesYes

Assign a role from a project's Users page โ€” pick a member, then translator, moderator, or admin. An admin on one project has no power over any other project, or over the account that owns it.

The super_admin flag

super_admin sits outside every project role โ€” an account-level flag that bypasses every check above, on every project. It is granted automatically to the first account created on an instance, whether through the local setup wizard or the first sign-in in server mode, and there is currently no way to promote another account to super_admin from inside the dashboard.

Webhooks

Configured per project from Admin โ†’ Integrations (super_admin only). Each webhook posts to your endpoint whenever a subscribed event happens on that project.

Events

A webhook can subscribe to any of the following:

  • key.deleted โ€” one or more translation keys were deleted
  • translation.saved โ€” a translation value was saved
  • scan.completed โ€” a source-code scan for new or changed keys finished
  • sync.completed โ€” a sync with the configured translation provider finished

Verifying a delivery

Every delivery to a generic HTTP endpoint carries X-Webhook-Signature โ€” an HMAC-SHA256 of the JSON body, using the secret shown once when the webhook was created โ€” and X-Webhook-Event. Slack and Discord endpoint URLs are detected automatically and receive that platform's own message format instead; those two authenticate via the URL itself, so they never carry a signature header.

Outbound URL requirements

  • Must resolve to a public address โ€” loopback, private (RFC1918/unique-local), and link-local targets are rejected, both when you save the URL and again on every delivery attempt (including each redirect hop), which closes the gap where a hostname resolves differently between the two.
  • In production, the endpoint must use https: โ€” plain HTTP is only tolerated outside production.
  • Credentials embedded in the URL (https://user:pass@host) are rejected.
  • A delivery times out after 10 seconds and follows at most 5 redirects.

Use the โ–ถ button next to a webhook to send a signed ping event on demand, without waiting for a real one.

Backup & restore

super_admin only, from Admin โ†’ Backup or the CLI. A backup is a single JSON export covering every project, translation, user, and setting.

Always excluded: refresh_tokens, password_reset_tokens, otp_tokens, webhook_deliveries, and every account's password hash.

From the dashboard

"Download backup" saves a dated JSON file, e.g. i18n-dashboard-backup-2026-07-31.json.

Restoring uploads that file back and asks for a mode:

  • merge (default) keeps existing rows and only inserts what's missing
  • replace deletes each table's existing data first, then imports โ€” irreversible

The result reports rows restored, duplicates skipped, and any row that failed, rather than silently reporting success on a partial restore.

From the CLI

bash

Both commands need an elevated admin token โ€” NUXT_CLI_ADMIN_TOKEN (falling back to NUXT_CLI_TOKEN) โ€” passed with --token, or resolved from your CLI config.

Troubleshooting

The issues self-hosters run into most, straight from the Docker image's own troubleshooting guide.

  • Refusing to start: NUXT_DB_HOST is still the build-time default 'localhost' โ€” your environment still uses pre-NUXT_* names. Add the NUXT_ prefix to your DB env vars (NUXT_DB_HOST, NUXT_DB_USER, โ€ฆ).
  • Webapp returns 401 on every API call โ€” the JWKS endpoint isn't reachable. Check that NUXT_CENTRAL_AUTH_URL resolves from inside the container (use the Docker network hostname when both services run in the same compose).
  • JWT verification fails right after a key rotation โ€” the JWKS cache lives 1 hour and refreshes 30 seconds after a key-id miss. Give it a short grace period, or restart the webapp right after rotating.
  • Health check shows "0 keys" despite a non-empty database โ€” you're probably hitting the wrong database. Confirm NUXT_DB_HOST is actually set, rather than falling back to a leftover SQLite file.
  • Container starts on port 3000 instead of 6001 โ€” Nitro reads PORT directly. The image sets I18N_PORT=6001, but overriding PORT moves the bind โ€” keep it aligned with your ports: mapping.
  • Can't reach the dashboard from another machine โ€” expected with the default Compose snippet โ€” the port binds to 127.0.0.1 on purpose. Bind a routable address, or put a reverse proxy in front, to expose it deliberately.