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

# Docker Compose

> Run Entri on a single host with Docker Compose: MongoDB, Redis, automatic HTTPS, search, email, backups and upgrades.

The Compose bundle in [`deploy/docker`](https://github.com/nt3-io/platform/tree/main/deploy/docker) runs Entri with MongoDB and Redis next to it. Optional profiles add Caddy for automatic HTTPS and Typesense for full-text search.

## What runs

| Service | Image | Purpose |
| - | - | - |
| `migrate` | `ghcr.io/nt3-io/entri` | Applies database migrations, then exits. `entri` starts only after it succeeds. |
| `entri` | `ghcr.io/nt3-io/entri` | Web app, REST and GraphQL API, and auth, on port 3333. |
| `mongo` | `mongo:7` | Single-node replica set `rs0` with keyfile auth. The keyfile is generated on first start and kept in the data volume. The healthcheck initiates the replica set on first boot. |
| `redis` | `redis:7-alpine` | Password-protected, with append-only persistence. |
| `caddy` | `caddy:2` | Profile `tls`. Reverse proxy with Let's Encrypt certificates for `ENTRI_DOMAIN`. |
| `typesense` | `typesense/typesense:0.27.1` | Profile `search`. Full-text search. |

MongoDB, Redis and Typesense publish no host ports. Only containers on the Compose network can reach them.

## Prerequisites

* A Linux host with Docker Engine 24+ and the Compose plugin (`docker compose version`)
* `git` and `openssl`
* For HTTPS: a DNS record (for example `entri.example.com`) pointing at the host, with ports 80 and 443 open

## Install

**1. Get the Compose bundle**

```bash theme={null}
git clone https://github.com/nt3-io/platform.git entri
cd entri/deploy/docker
cp .env.example .env && chmod 600 .env
```

You only need the three files in `deploy/docker` (`docker-compose.yml`, `.env.example`, `Caddyfile`). You can copy them anywhere.

**2. Generate secrets**

Generate each value and paste it into `.env`:

```bash theme={null}
openssl rand -hex 24      # MONGO_ROOT_PASSWORD
openssl rand -hex 24      # REDIS_PASSWORD
openssl rand -base64 48   # BETTER_AUTH_SECRET
openssl rand -hex 32      # ENCRYPTION_KEY
```

Or fill them in with one command:

```bash theme={null}
sed -i \
  -e "s|^MONGO_ROOT_PASSWORD=.*|MONGO_ROOT_PASSWORD=$(openssl rand -hex 24)|" \
  -e "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -hex 24)|" \
  -e "s|^BETTER_AUTH_SECRET=.*|BETTER_AUTH_SECRET=$(openssl rand -base64 48)|" \
  -e "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$(openssl rand -hex 32)|" \
  .env
```

(On macOS, use `sed -i ''`.)

> [!WARNING]
>
> Use URL-safe passwords for MongoDB and Redis: they become part of the connection strings. The hex values above are safe. Back up `ENCRYPTION_KEY` now. Without it, Entri cannot decrypt the AI keys that organizations store.

**3. Set the public URL and version**

In `.env`, set the URL users open in the browser, with no trailing slash, and pin a release:

```bash theme={null}
ENTRI_VERSION=1.2.3                      # a tag from ghcr.io/nt3-io/entri
BETTER_AUTH_URL=https://entri.example.com
APP_HOST=https://entri.example.com
ENTRI_DOMAIN=entri.example.com           # only used by the Caddy profile
```

Replace `1.2.3` with the [latest release](https://github.com/nt3-io/platform/releases). `ENTRI_VERSION=latest` works too, but then `docker compose pull` can move you to a new major version.

**4. Start the stack**

Choose how to expose Entri:

> [tab] With HTTPS (Caddy)
>
> Keep port 3333 off the public interface and let Caddy terminate TLS:
>
> ```bash theme={null}
> # in .env
> ENTRI_PORT=127.0.0.1:3333
> ```
>
> ```bash theme={null}
> docker compose --profile tls up -d
> ```
>
> Caddy requests a Let's Encrypt certificate for `ENTRI_DOMAIN` on first start. DNS must already point at this host.

> [tab] Behind your own proxy
>
> ```bash theme={null}
> docker compose up -d
> ```
>
> Entri listens on `ENTRI_PORT` (default `3333`, all interfaces). Point your proxy at it. See [Using your own reverse proxy](#using-your-own-reverse-proxy).

Wait until `entri` is healthy:

```bash theme={null}
docker compose ps
docker compose logs -f migrate entri
```

**5. Create the first account**

Open `https://entri.example.com` and sign up. The first account on an empty instance can always sign up. Then create your organization and first project.

To stop strangers from signing up, set this in `.env` and apply it:

```bash theme={null}
DISABLE_SIGNUP=true
```

```bash theme={null}
docker compose up -d
```

Invited users can still create accounts.

> [!TIP]
>
> Profiles must be passed on every `docker compose` command, or the services they add are left out. To avoid that, put them in `.env`: `COMPOSE_PROFILES=tls,search`.

## TLS and reverse proxies

### Bundled Caddy

The `tls` profile runs Caddy with this `Caddyfile`:

```text theme={null}
{$ENTRI_DOMAIN} {
	reverse_proxy entri:3333
}
```

Caddy proxies WebSockets and streams Server-Sent Events without extra configuration. Certificates are stored in the `caddy_data` volume.

### Using your own reverse proxy

Any proxy works if it:

* passes WebSocket upgrades for the progress channel at `/ws/progress`
* does **not buffer** `text/event-stream` responses and allows long-lived connections: imports, AI batch translation and GitHub sync stream their progress as Server-Sent Events
* accepts request bodies of at least 10 MB (file imports)
* sets `X-Forwarded-For` and `X-Forwarded-Proto`

Do not add response compression in the proxy. Entri compresses responses itself and skips event streams.

Example for nginx:

```nginx theme={null}
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl http2;
    server_name entri.example.com;

    ssl_certificate     /etc/ssl/entri.example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/entri.example.com/privkey.pem;

    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:3333;
        proxy_http_version 1.1;

        # WebSockets
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Server-Sent Events
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

If the proxy runs on the same host or a private network, the default `TRUST_PROXY` already trusts it. If it has a public IP (for example a cloud load balancer), set `TRUST_PROXY` to its address range or a hop count. See [Server settings](https://docs.nt3.io/self-hosting/configuration.md#server).

## Search (Typesense)

Without a search provider, Entri searches with MongoDB queries. To use the bundled Typesense:

**1. Configure it in .env**

```bash theme={null}
SEARCH_PROVIDER=typesense
TYPESENSE_HOST=typesense
TYPESENSE_PORT=8108
TYPESENSE_PROTOCOL=http
TYPESENSE_API_KEY=<output of: openssl rand -hex 24>
```

> [!WARNING]
>
> Always set `TYPESENSE_API_KEY`. If it is empty, the Typesense container starts with the key `change-me`.

**2. Start it**

```bash theme={null}
docker compose --profile search up -d     # add --profile tls if you use Caddy
```

With the profile active, Entri waits until Typesense passes its healthcheck. This uses `depends_on` with `required: false`, which needs Docker Compose 2.20 or later.

**3. Index existing projects**

New and changed keys are indexed as you work. Keys that existed before you enabled search must be indexed once per project, as a signed-in user or with an [API token](https://docs.nt3.io/developers/api-tokens.md) from the project's organization:

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

To use Algolia instead, set `SEARCH_PROVIDER=algolia`, `ALGOLIA_APP_ID` and `ALGOLIA_API_KEY`, and run the same sync.

## Email

Without an email transport, Entri writes emails (invitations, password resets) to the container log. To send them, configure SMTP or Resend in `.env`:

```bash SMTP theme={null}
EMAIL_PROVIDER=smtp
EMAIL_FROM=Entri <noreply@example.com>
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false        # true for port 465
SMTP_USER=apikey
SMTP_PASS=...
```

```bash Resend theme={null}
EMAIL_PROVIDER=resend
EMAIL_FROM=Entri <noreply@example.com>
RESEND_API_KEY=re_...
```

Then run `docker compose up -d`. Entri refuses to start if the chosen provider is missing `SMTP_HOST`, `RESEND_API_KEY` or `EMAIL_FROM`. Check `docker compose logs entri` after a change.

## Backups

All durable data is in MongoDB. The commands below are for the **bundled** `mongo` container (the image ships the database tools) and dump only the `entri` database. If you use an [external MongoDB](#external-mongodb-or-redis), back it up with your provider's backups, or run `mongodump --uri "$MONGO_URI" --archive --gzip > entri-$(date +%F).archive.gz` from a machine with the MongoDB database tools.

Bundled MongoDB:

```bash theme={null}
docker compose exec -T mongo sh -c \
  'mongodump --quiet --archive --gzip --db entri \
     -u "$MONGO_INITDB_ROOT_USERNAME" -p "$MONGO_INITDB_ROOT_PASSWORD" \
     --authenticationDatabase admin' \
  > entri-$(date +%F).archive.gz
```

To restore into the same stack:

```bash theme={null}
docker compose stop entri
docker compose exec -T mongo sh -c \
  'mongorestore --quiet --archive --gzip --drop \
     -u "$MONGO_INITDB_ROOT_USERNAME" -p "$MONGO_INITDB_ROOT_PASSWORD" \
     --authenticationDatabase admin' \
  < entri-2026-01-31.archive.gz
docker compose start entri
```

Also keep a copy of `.env`. It holds `ENCRYPTION_KEY` and `BETTER_AUTH_SECRET`, which a database backup cannot replace. Store it separately from the database dumps.

You do not need to back up:

* **Redis**: it holds caches and AI batch-job state that expire within minutes, locks, and progress messages. After a loss, Entri rebuilds what it needs.
* **Typesense**: rebuild it with the [search sync](#search-typesense).
* **Caddy**: certificates are requested again when needed.

## Logs and health

```bash theme={null}
docker compose logs -f entri         # application log
docker compose logs migrate          # output of the last migration run
docker compose ps                    # health status of every service
curl -s http://127.0.0.1:3333/api/v1/health/readiness
```

The readiness endpoint checks MongoDB and Redis. Entri also logs configuration warnings at startup, for example when `ENCRYPTION_KEY` is missing or `BETTER_AUTH_URL` points at localhost.

## External MongoDB or Redis

`docker-compose.yml` always starts the bundled `mongo` and `redis` containers and waits for them. To use managed databases, use the standalone `docker-compose.external.yml` in the same directory. It runs only `migrate`, `entri` and the optional `caddy` (`--profile tls`), with no MongoDB or Redis containers.

Set the connection strings in `.env` (`MONGO_ROOT_PASSWORD` and `REDIS_PASSWORD` are not needed):

```bash theme={null}
MONGO_URI=mongodb+srv://user:pass@cluster.example.net/entri
REDIS_URI=redis://:password@redis.example.net:6379
```

Then start the stack:

```bash theme={null}
docker compose -f docker-compose.external.yml up -d
docker compose -f docker-compose.external.yml --profile tls up -d   # with Caddy
```

* `MONGO_URI` must include the database name in the path (`/entri`). Migrations refuse to run without it. See [Database settings](https://docs.nt3.io/self-hosting/configuration.md#database).
* A replica set (or Atlas) is recommended. A standalone `mongod` works with `MONGO_TRANSACTIONS=false`, at the cost of weaker guarantees on auth writes.
* Pass `-f docker-compose.external.yml` to every `docker compose` command, including `pull`, `logs` and `ps`.

## Building from source

In `docker-compose.yml`, replace the `image:` line under `x-entri` with the commented `build:` block:

```yaml theme={null}
x-entri: &entri
  # image: ghcr.io/nt3-io/entri:${ENTRI_VERSION:-latest}
  build:
    context: ../..
    dockerfile: Dockerfile
```

Then build and start:

```bash theme={null}
docker compose up -d --build
```

The build needs no secrets. It compiles the API and web app, so give Docker a few GB of memory.

## Upgrading

```bash theme={null}
# set ENTRI_VERSION in .env to the new release, then:
docker compose pull
docker compose up -d
```

The `migrate` service applies migrations before the new `entri` container starts. Back up first. See [Upgrading](https://docs.nt3.io/self-hosting/upgrading.md).


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