> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nt3.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from Entri Cloud

> Move your organization from the hosted service at app.nt3.io to your own Entri instance.

The hosted Entri service at app.nt3.io is being retired. Entri is now open source, and you keep using it by running your own instance. This guide moves your organization, with its history intact, from app.nt3.io to that instance.

> [!WARNING]
>
> **Timeline**
>
> * **30 November 2026**: last day to request a full export of your organization, and the day app.nt3.io shuts down. After that date, hosted data can no longer be exported.
>
> Request your export from the Entri team at **[bram@xlab.be](mailto:bram@xlab.be)** well before the deadline. Exports are prepared by hand.

There are two ways to move:

| | Full migration | Lightweight migration |
| - | - | - |
| **How** | The Entri team exports your organization from the hosted database, and you import it into yours | You pull files with the `nt3` CLI from app.nt3.io and push them to your instance |
| **Keeps** | Everything: projects, keys, translations with their statuses, history, comments, activity log, glossaries, translation memory, members, API tokens, webhooks | Keys and translation values. Glossaries through CSV export and import. |
| **Effort** | A few commands, plus a short checklist | One project at a time, and some settings to redo by hand |

## Full migration

### What the export contains

The export is an archive with your organization's data. Every document keeps its original ID, so all references survive the move.

| Included | Not included |
| - | - |
| Organization, members, pending invitations | **Sessions**: everyone signs in again |
| Users who are members of your organization | **Passwords**, unless you agree otherwise with the Entri team (see below) |
| Projects, keys, translations, translation and key history | **AI provider keys**: removed from the export. You enter them again. |
| Comments and the activity log | **Webhook signing secrets**: replaced, and webhooks are deactivated. You re-create them. |
| Glossaries and terms, translation memory | Password-reset and verification tokens |
| Webhooks (deactivated, see above) | Search indexes (Typesense or Algolia), Redis caches |
| API tokens (stored as hashes, so **existing tokens keep working**) | The connection to the hosted GitHub App |
| Organization settings, project GitHub settings (repository, branch, mode) | |

**Passwords.** By default, the export contains no password hashes or OAuth tokens. Users set a new password with **Forgot password** on your instance, which needs [email](#post-import-checklist) configured, or sign in with Google or GitHub if you enable them. If you want users to keep their passwords, ask for an export with credentials when you request it. Treat that archive like a password database.

Changes made on app.nt3.io after the export are not included. Agree on a cut-off time with your team, and stop working on the hosted instance at that point.

### Steps

**1. Deploy your instance**

Deploy Entri with [Docker Compose](https://docs.nt3.io/self-hosting/docker.md) or [Kubernetes](https://docs.nt3.io/self-hosting/kubernetes.md). Use the same version as the hosted service or newer. Set `ENCRYPTION_KEY` and configure [email](https://docs.nt3.io/self-hosting/configuration.md#email).

> [!WARNING]
>
> **Do not sign up or create any users yet.** The import refuses to run if an account with one of your users' email addresses already exists on the instance. If you already created a test account, delete it or start with a fresh database.

Make sure migrations have run. With Docker Compose, the `migrate` service runs them on `docker compose up -d`. With Helm, the migration hook runs them on install. You can also run them by hand:

```bash theme={null}
docker compose run --rm entri migrate
```

**2. Request your export**

Contact the Entri team at [bram@xlab.be](mailto:bram@xlab.be). Tell them:

* your organization's name or slug on app.nt3.io
* whether users should keep their passwords (an export with credentials)
* a public key or other secure channel for receiving the archive

You receive an encrypted archive, for example `acme-2026-01-31.tar.gz.age`, with the password or key sent separately. Decrypt it (`age -d -i key.txt acme-2026-01-31.tar.gz.age > acme.tar.gz`, or as instructed) and keep it private: it contains all your data.

**3. Get the import tool**

The tool is in the Entri repository, in `tools/org-migration`. It is plain Node.js, needs Node.js 20.19 or later, `tar`, and the `mongodb` driver, and it writes directly to your instance's MongoDB.

```bash theme={null}
git clone https://github.com/nt3-io/platform.git entri-src
cd entri-src
```

**4. Import**

Use the same connection string as your instance's `MONGO_URI`, including the database name. Run a dry run first: it checks everything and writes nothing.

> [tab] Docker Compose
>
> MongoDB is only reachable inside the Compose network (`entri_default`), so run the tool in a throwaway Node container. Put the archive in the current directory and replace `PASS` with `MONGO_ROOT_PASSWORD` from your `.env`:
>
> ```bash theme={null}
> export MONGO_URI='mongodb://entri:PASS@mongo:27017/entri?authSource=admin&replicaSet=rs0'
>
> docker run --rm -it --network entri_default \
>   -e MONGO_URI \
>   -v "$PWD/tools/org-migration:/tool:ro" -v "$PWD:/data" -w /tmp node:22 \
>   sh -c 'npm i --no-save --no-package-lock mongodb@7 >/dev/null && \
>          cp -r /tool/. . && \
>          node cli.cjs import --uri "$MONGO_URI" --in /data/acme.tar.gz --dry-run && \
>          node cli.cjs import --uri "$MONGO_URI" --in /data/acme.tar.gz && \
>          node cli.cjs verify --uri "$MONGO_URI" --in /data/acme.tar.gz'
> ```

> [tab] Kubernetes (bundled MongoDB)
>
> Forward the MongoDB port and run the tool locally:
>
> ```bash theme={null}
> kubectl -n entri port-forward svc/entri-mongodb 27017:27017 &
> PASS=$(kubectl -n entri get secret entri-mongodb -o jsonpath='{.data.mongodb-root-password}' | base64 -d)
> export MONGO_URI="mongodb://entri:$PASS@127.0.0.1:27017/entri?authSource=admin&directConnection=true"
>
> pnpm install --frozen-lockfile    # only the mongodb driver is actually needed
> node tools/org-migration/cli.cjs import --uri "$MONGO_URI" --in ./acme.tar.gz --dry-run
> node tools/org-migration/cli.cjs import --uri "$MONGO_URI" --in ./acme.tar.gz
> node tools/org-migration/cli.cjs verify --uri "$MONGO_URI" --in ./acme.tar.gz
> ```

> [tab] Managed MongoDB
>
> Run the tool from any machine that can reach your database:
>
> ```bash theme={null}
> pnpm install --frozen-lockfile    # only the mongodb driver is actually needed
> node tools/org-migration/cli.cjs import --uri "$MONGO_URI" --in ./acme.tar.gz --dry-run
> node tools/org-migration/cli.cjs import --uri "$MONGO_URI" --in ./acme.tar.gz
> node tools/org-migration/cli.cjs verify --uri "$MONGO_URI" --in ./acme.tar.gz
> ```

The import refuses to write anything when:

* your instance is **behind** the export on database migrations. Upgrade Entri and run `migrate` first. If your instance is *ahead*, add `--allow-newer-target`.
* the organization already exists on the instance. Add `--force` to update it: it upserts by ID and never deletes anything.
* another organization on the instance uses the same slug
* a user on the instance has the same email as an imported user but a different ID. The tool lists every conflict.

Writes are idempotent, so if an import is interrupted, run it again.

**5. Restart Entri and sign in**

```bash theme={null}
docker compose restart entri                    # Docker Compose
kubectl -n entri rollout restart deploy/entri   # Kubernetes
```

Everyone signs in again. Without an export with credentials, users choose **Forgot password** first.

### Post-import checklist

**1. Email**

Configure SMTP or Resend before you tell users to sign in, so password resets and invitations arrive. See [Email](https://docs.nt3.io/self-hosting/configuration.md#email).

**2. Close sign-up**

Your users now exist, so set `DISABLE_SIGNUP=true` to allow new accounts by invitation only.

**3. AI provider keys**

An owner or admin enters the OpenAI and Anthropic keys again in **Organization settings**. They are encrypted with your instance's `ENCRYPTION_KEY`.

**4. Webhooks**

Imported webhooks are deactivated, and their signing secrets were replaced with unknown values. A secret is shown only when a webhook is created, so delete each webhook, [create it again](https://docs.nt3.io/platform/integrations/webhooks.md), and put the new secret in your receiver.

**5. GitHub sync**

The imported connection points at the hosted GitHub App, which does not work with your instance. [Register your own GitHub App](https://docs.nt3.io/platform/integrations/github-app-setup.md), install it, and reconnect under **Settings → Integrations**. The repository, branch and mode settings on your projects are kept.

**6. Social login**

To offer "Sign in with Google" or "Sign in with GitHub", create your own OAuth apps with callback URLs on your domain and set `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` and `GITHUB_CLIENT_ID`/`GITHUB_CLIENT_SECRET`. See [Social login](https://docs.nt3.io/self-hosting/configuration.md#social-login). The hosted OAuth apps only work for app.nt3.io.

**7. Search index**

Skip this if you use the default MongoDB search. With Typesense or Algolia, rebuild each project's index as a signed-in user or with an API token of the organization:

```bash theme={null}
curl -X POST -H "Authorization: Bearer $NT3_API_TOKEN" \
  https://entri.example.com/api/v1/admin/search/sync/<projectId>
```

**8. CLI**

Existing API tokens keep working, but the CLI must point at your instance. Each developer logs in again:

```bash theme={null}
nt3 login --api-url https://entri.example.com
```

To point everyone on a project at your instance, add this to `.nt3.yml`:

```yaml theme={null}
api_url: https://entri.example.com
```

The CLI only sends a stored token to the URL it was saved for, so a developer who logged in to app.nt3.io gets a clear error until they log in again.

**9. CI pipelines**

Set `NT3_API_URL` next to `NT3_API_TOKEN`. The existing token values keep working:

```yaml theme={null}
env:
  NT3_API_TOKEN: ${{ secrets.NT3_API_TOKEN }}
  NT3_API_URL: https://entri.example.com
```

`NT3_API_TOKEN` is only sent to `NT3_API_URL`. Without it, the CLI keeps calling app.nt3.io.

**10. Delete the archive**

Once everything works, delete the archive and any decrypted copies.

## Lightweight migration

If you only need your keys and translations, you can move them yourself with the CLI, project by project, without an export from the Entri team.

> [!WARNING]
>
> This path loses data. On the new instance:
>
> * **Translation statuses** are reset. Every imported translation becomes *Translated*: *Reviewed* and *Approved* states are lost.
> * **History, comments and the activity log** are not carried over.
> * **Translation memory** is not carried over.
> * **Key metadata** your file format cannot express (for example descriptions or context in a JSON file) is lost.
> * **Members, API tokens, webhooks, project settings** (such as AI tone and context) and the GitHub connection must be set up again.
> * Glossary **part of speech and notes** are not part of the CSV export.

**1. Set up the new instance**

Deploy your instance, sign up, and create your organization. For each project, create a project with the same source language. Create an [API token](https://docs.nt3.io/developers/api-tokens.md) on the new instance.

**2. Pull from app.nt3.io**

In the repository that holds your `.nt3.yml`, with a token from app.nt3.io:

```bash theme={null}
NT3_API_URL=https://app.nt3.io NT3_API_TOKEN=<hosted token> nt3 pull
```

This writes the source language and every target language to the paths in `.nt3.yml`.

**3. Point the project at your instance**

In `.nt3.yml`, replace `project_id` with the new project's ID and add `api_url`:

```yaml theme={null}
api_url: https://entri.example.com
project_id: "<new project id>"
```

**4. Push to your instance**

```bash theme={null}
NT3_API_URL=https://entri.example.com NT3_API_TOKEN=<new token> nt3 push
```

`push` uploads the source file first, then every target-language file it finds, and enables those languages in the project.

**5. Move glossaries**

On app.nt3.io, open **Glossary**, select a glossary and choose **Export as CSV**. On your instance, create a glossary with the same name and choose **Import terms (CSV or TBX)**.

**6. Redo the rest**

Invite your team, re-create webhooks, add AI keys, and update CLI and CI configuration as in the [post-import checklist](#post-import-checklist).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.