Skip to main content

Self-host it (fully free)

i18n-keyless is one product, and you can run it yourself: the same API, the same dashboard, the same MCP server as i18n-keyless.com — it is the very Docker image we run — on your own machine, with your own database and your own AI provider.

The source is public, under the Elastic License 2.0: github.com/ambroselli-io/i18n-keyless-server. Read it, build the image yourself, or send a pull request.

Fully free: unlimited projects, all 48 languages, unlimited translations, the dashboard, the MCP server, every SDK, no expiry. Nothing to buy, nothing to enter but your own AI key.

If you like it, consider a donation at https://i18n-keyless.com/self-hosted — any amount from 1 to 1 million dollars. The product is fully available without it. A donation gives you a licence key (starts with ik1.) that silences the donation prompt in the dashboard.

ONCE is 37signals' open source tool for running Docker apps on your own server: one line to install, then a terminal console for the domain, the TLS certificate, updates and backups. i18n-keyless follows its conventions, so it is a first-class app there.

On a fresh Linux server:

curl -fsSL https://get.once.com | sh
once

In the console, add an app, enter the image path, and the host name you want it on:

ghcr.io/ambroselli-io/i18n-keyless:latest

ONCE pulls the image, starts it, and takes care of DNS and TLS. Open the host name in your browser:

  1. Create your account. The first account of an instance is its admin: it creates projects and invites people.
  2. Choose the AI provider and paste your key at that provider, on the Settings screen you land on. Test the key asks the provider once before you save.
  3. Create your project. No licence needed — projects are unlimited.

That is the whole install. The dashboard says when the instance is ready, and what is still missing until then.

With docker compose​

No ONCE? One file and Docker:

mkdir i18n-keyless && cd i18n-keyless
curl -fsSL https://i18n-keyless.com/self-hosted/docker-compose.yml -o docker-compose.yml
docker compose up -d
open http://localhost:1800

Then the same steps in the browser: the admin account, the AI key, your project.

Put your usual reverse proxy (Caddy, nginx, Traefik) with TLS in front of port 1800 first if you can: the instance learns its public URL from your first sign-in. Moved it later? The Settings screen has the URL too.

Settings: the dashboard, or the environment​

The licence, the AI provider and the public URL live on the dashboard's Settings screen (admins only), and are kept in /storage/settings.json, next to the database and inside the backup. A change applies at once, no restart.

Each of them can also be set in the environment — an .env next to docker-compose.yml (the example), or ONCE's Settings → Environment form. A value from the environment wins, and the Settings screen shows it read-only with the variable's name. Use that to lock a setting, or to script an install; leave it out and the dashboard is enough.

The AI provider​

Translations are made by the model you choose, on your own account. Any provider the Vercel AI SDK knows — pick it on the Settings screen, or name it in the environment:

AI_PROVIDERAI_MODEL (default)Note
mistralmistral-small-latestWhat i18n-keyless.com runs on.
openaigpt-4o-mini
anthropicclaude-haiku-4-5-20251001
googlegemini-2.0-flash
openai-compatiblellama3.1Ollama, Groq, Together, a proxy… set AI_BASE_URL (default http://localhost:11434/v1).

AI_API_KEY is the key at that provider. A whole app costs cents.

When the provider refuses​

A wrong key, no credits left, a model that does not exist: the provider refuses, and new texts stay untranslated — your app shows them in its source language, as always. Nothing reaches your users, not even in the browser console. Instead:

  • the dashboard shows a red banner to the instance's admins, and Settings → AI provider shows what the provider said;
  • if you set a webhook in Settings → Alerts (or ALERT_WEBHOOK_URL), the instance POSTs one message when the refusals start and one when translations work again. Slack and Discord webhooks, an ntfy.sh topic (a free push to your phone), or any URL that takes a JSON body. Send a test alert checks it before you save.

These refusals are yours to fix, so they are not reported to the maintainers' Sentry.

Accounts and projects​

  • The first account created on an empty instance is the admin. Admins create projects and can make other users admin (PUT /user/admin).
  • Everyone else uses the projects they are invited to. When you add a member, the dashboard shows the invitation message to send them yourself (with the set-password link); the instance sends no email, so there is no mail provider to configure.
  • Pausing a project (admins) stops its key; nothing is deleted.

Point your app at your server​

Every SDK takes an API_URL. Add it next to the key:

import * as I18nKeyless from 'i18n-keyless-react';

I18nKeyless.init({
API_KEY: 'your-project-key',
API_URL: 'https://i18n.example.com', // your APP_URL
storage: window.localStorage,
languages: { primary: 'en', supported: ['en', 'fr', 'es'] },
});

The dashboard's snippets already carry it. Node, Vue, Angular and the browser package take the same option; Laravel, Rails and Flutter read it from their own config. The MCP server is at https://i18n.example.com/mcp, and the onboarding screen prints the claude mcp add line.

Upgrade, back up​

ONCE updates and backs up from its console. With compose: docker compose pull && docker compose up -d; the schema is applied at boot and the volume is kept. The data lives in /storage: i18n-keyless.db (SQLite: users, projects, translations), settings.json (the AI key, the URL, the licence if you bought one) and secret (signs the sessions). Copy the volume and you have everything.

Prefer Postgres? Set DATABASE_URL=postgresql://…; the image ships both drivers.

The instance does not update itself (ONCE excepted) and does not tell you a new version exists. To hear about the ones that matter, see below.

Product updates by email​

Settings → Product updates: give an address to get notified of important updates — security updates, bug fixes. A few emails a year, no marketing. You confirm it with a link we email you (nothing is sent before), and every email has a link to unsubscribe. The address goes to the maintainer of i18n-keyless and nowhere else.

What your instance sends us​

At each start, one request to api.i18n-keyless.com/self-hosted/ping with:

  • a random install id, made once and kept in /storage/.install-id;
  • the version;
  • how it runs: once, docker, kubernetes, podman or other;
  • the database kind (sqlite or postgres, never the URL);
  • the AI provider's name (never the key);
  • the CPU architecture and the Node version.

We look up the country of the request's IP (through country.is) and do not keep the IP. TELEMETRY=off stops this ping. The product-update address, if you gave one, is sent too, and still is with TELEMETRY=off: you asked for it. Error reports are SENTRY_KEY's, below.

What is different from the hosted product​

Nothing in the software: it is the same image. What differs is who operates it.

HostedSelf-hosted
PricePer project, per monthFully free, donations optional
Machine, database, AI accountOursYours
ProjectsOne per subscription, created from the websiteUnlimited, created by your admin
SupportEmailEmail, best effort

Environment reference​

Nothing is required: the first five are the Settings screen's, and a variable only locks the value. The instance boots and answers its health check (GET /up) with none of them.

VariableOn the Settings screenDefaultWhat it does
LICENSE_KEYyes—Your licence key from a donation, and it is optional. Verified offline. The product is fully free without it; the key only silences the donation prompt in the dashboard.
AI_PROVIDERyesmistralSee the table above.
AI_MODELyesthe provider's default
AI_API_KEYyes—Your key at the provider.
AI_BASE_URLyes—For openai-compatible, or a proxy.
ALERT_WEBHOOK_URLyes—Where to POST when the AI provider refuses, and when it works again.
APP_URLyeslearnt from the first sign-inThe public URL: cookie security, OAuth issuer, invitation links.
DATA_DIR—/storageWhere settings.json, secret and the SQLite file live.
DATABASE_URL—file:/storage/i18n-keyless.dbSQLite file, or a Postgres URL.
SECRET—generated once, kept in /storage (ONCE's SECRET_KEY_BASE is honoured)JWT secret of the dashboard sessions.
TELEMETRY—onoff stops the start-up ping described above.
SENTRY_KEY—the maintainer'sWhere errors are reported. By default they reach us, the maintainers — that is how bugs get fixed. off disables it; your own DSN redirects it.

Lost your licence key?​

Write to us with the email you bought it with. There is nothing to revoke or renew: a licence is bound to that email and is valid forever.