> ## 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.

# Self-hosting Entri

> Run your own Entri instance with Docker — local development or production.

Entri is open source and designed to be self-hostable. The same codebase that powers [app.nt3.io](https://app.nt3.io) can run on your own infrastructure with no managed service dependencies.

## Quickstart (local Docker)

For evaluating Entri or running it on a laptop:

```bash theme={null}
git clone https://github.com/nt3-io/platform.git entri
cd entri
cp .env.example .env
docker compose up -d
pnpm install
pnpm migrate
pnpm dev:api      # API on http://localhost:3333
pnpm dev          # Web app on http://localhost:4200
```

`docker compose up` boots three containers — MongoDB, Redis, and TypeSense — bound to `127.0.0.1` only, with persistent named volumes. That covers everything Entri needs to run locally; no external accounts required.

<Note>
  The local Compose stack runs without authentication and binds to loopback. **Do not** expose those ports to the internet or to a shared LAN.
</Note>

## Architecture overview

Entri is an Nx monorepo with three deployable apps:

| App        | Stack                   | Default port |
| ---------- | ----------------------- | ------------ |
| `apps/api` | NestJS (REST + GraphQL) | 3333         |
| `apps/app` | React + Vite            | 4200         |
| `apps/cli` | NestJS Commander        | n/a          |

Runtime dependencies:

* **MongoDB** — primary datastore (Mongoose 8)
* **Redis** — distributed cache and websocket pub/sub
* **TypeSense** *(or Algolia)* — full-text search (optional; falls back to MongoDB regex)

## Configuration

All configuration is via environment variables. Copy `.env.example` to `.env` and fill in what you need. The minimum required for the app to boot:

```bash theme={null}
APP_HOST=https://entri.example.com
NX_BACKEND_URL=https://entri.example.com
BETTER_AUTH_SECRET=<32+ char secret — `openssl rand -base64 32`>
BETTER_AUTH_URL=https://entri.example.com
MONGO_URI=mongodb://...
REDIS_URI=redis://...
```

Optional but recommended:

```bash theme={null}
# Search backend — choose one
SEARCH_PROVIDER=typesense
TYPESENSE_HOST=typesense.internal
TYPESENSE_API_KEY=...

# AI translation — at least one provider
ANTHROPIC_API_KEY=...
OPENAI_API_KEY=...
GOOGLE_GENERATIVE_AI_API_KEY=...

# Outbound email — see "Outbound email" section below
EMAIL_FROM="Entri <noreply@example.com>"
RESEND_API_KEY=...
# or SMTP_HOST=..., SMTP_PORT=..., SMTP_USER=..., SMTP_PASS=...

# OAuth login
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
```

See `.env.example` in the repo for the complete list with inline documentation.

## Search backend

Set `SEARCH_PROVIDER` to pick a search backend. Each option targets a different deployment shape:

| Value       | Backend                                        | Best for                                                         |
| ----------- | ---------------------------------------------- | ---------------------------------------------------------------- |
| `typesense` | Self-hosted [TypeSense](https://typesense.org) | Self-hosters who want full-text search without a SaaS dependency |
| `algolia`   | Hosted [Algolia](https://www.algolia.com)      | Teams already on Algolia or those wanting zero search ops        |
| *(unset)*   | MongoDB regex fallback                         | Tiny instances or development — works but slow at scale          |

After enabling a real search backend for the first time, backfill existing data:

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

## Outbound email

Entri sends transactional email for invitations, welcome messages, and password resets. Two transports are supported. Pick whichever fits your stack — both produce identical output.

`EMAIL_FROM` is **required** whenever a real transport is configured. Use an address on a domain you control. If neither transport is configured, emails are logged to the console (useful for local dev).

| Transport  | Set                                                | Best for                                                        |
| ---------- | -------------------------------------------------- | --------------------------------------------------------------- |
| **Resend** | `RESEND_API_KEY`                                   | Cloud deployments where you want a managed sender               |
| **SMTP**   | `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS` | Self-hosters using their own MTA, AWS SES, Mailgun, Gmail, etc. |

When both are set, SMTP wins. Force a specific provider with `EMAIL_PROVIDER=resend` or `EMAIL_PROVIDER=smtp`.

```bash theme={null}
# Option A — Resend
EMAIL_FROM="Entri <noreply@example.com>"
RESEND_API_KEY=re_...

# Option B — SMTP (e.g. AWS SES on us-east-1)
EMAIL_FROM="Entri <noreply@example.com>"
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_SECURE=false        # true for port 465, false for 587/STARTTLS
SMTP_USER=AKIA...
SMTP_PASS=...
```

The API verifies the SMTP connection on startup and logs a warning if it fails — sends will still be attempted, so misconfigurations show up in your logs without crashing the boot.

## Production deployment

Entri ships with a multi-stage `Dockerfile` at the repo root that produces a single image containing both the API and the built web bundle. The reference deployment runs on Google Cloud Run, but the image is platform-agnostic — anywhere that runs OCI containers will work.

```bash theme={null}
docker build -t entri:latest .
docker run --rm \
  -p 3333:3333 \
  --env-file .env \
  entri:latest
```

For multi-instance deployments (recommended for production), you'll need:

1. **A managed MongoDB** — Atlas, DocumentDB, or self-hosted with replication.
2. **A managed Redis** — Memorystore, ElastiCache, Upstash, or self-hosted.
3. **A managed TypeSense** — [TypeSense Cloud](https://cloud.typesense.org) or self-hosted on the same network as the API.
4. **Static asset hosting** — the API serves the built web bundle by default; no separate hosting needed unless you want a CDN.
5. **A reverse proxy** with TLS termination (Cloud Run / ALB / nginx).

<Tip>
  The websocket progress streams (used by import/export) rely on Redis pub/sub for fan-out across instances. Make sure all API replicas talk to the same Redis.
</Tip>

## Running migrations

The API does not auto-migrate. Run `migrate-mongo up` against your MongoDB before starting a new version:

```bash theme={null}
MONGO_URI=mongodb://... pnpm migrate
```

In CI/CD, run this as a release-step job before rolling out the new image.

## Updating

```bash theme={null}
git pull
docker compose pull           # if using Compose locally
pnpm install
pnpm migrate
# rebuild and redeploy your image
```

## Going further

* [Contributing](/developers/contributing) — develop against the codebase locally
* [Authentication](/developers/authentication) — configure OAuth providers
* [API tokens](/developers/api-tokens) — issue API credentials
* [GitHub repository](https://github.com/nt3-io/platform) — source, issues, releases
