# Installing on your own server

You can run draften on your own server. Mail, drafts and keys then stay with
you; only what you send to the language model provider leaves (or nothing,
with a local model).

This page is an overview. **The detailed and always current procedure is in
the `README.md` of the draften repository** — follow it; here is only what is
good to know up front.

## What an installation is made of

Everything runs in Docker through `docker compose`:

| Service | What it does |
|---|---|
| `db` | the MariaDB database — holds everything: settings, instructions, messages, drafts, encrypted secrets |
| `worker` | reads the mailboxes, calls the models, writes drafts, learns, runs the wizard's steps. Several may run |
| `web` | the web interface and API (Symfony on FrankenPHP) |
| `cloudflared` | optionally: publishing the interface through a Cloudflare Tunnel |

The interface and the worker do not talk to each other directly — both work
with the same database; what needs mail or a model, the interface queues as a
job for the worker.

## What you will need

- a server with Docker (Docker Compose v2),
- **no Node.js or PHP on the server** — everything is in the images,
- access to the mailboxes: an app registration in Microsoft Entra, a Google
  Workspace service account, or IMAP details,
- a model API key (Anthropic, OpenAI) or your own OpenAI-compatible server,
- the address the interface will be reachable at, with HTTPS.

On Windows with Docker Desktop, keep it in autostart and turn *Resource Saver*
off.

## Settings in `.env`

The `.env` file (the template is `.env.example`) holds only infrastructure
and secrets. Everything about assistants is in the database and set in the
interface.

| Variable | What for |
|---|---|
| `DB_ROOT_PASSWORD`, `DB_PASSWORD` | database passwords |
| `MASTER_KEY` | the key encrypting the secrets in the database (API keys, mailbox access, clients). Generate with `openssl rand -hex 32` and **keep a copy off the server** — without it a database backup is unreadable |
| `WEB_APP_SECRET` | a random string signing sessions |
| `WEB_PUBLIC_URL` | the address users open the interface at; the redirect URIs are built from it |
| `CLOUDFLARE_TUNNEL_TOKEN` | only with a Cloudflare Tunnel |
| `INTERVAL_SECONDS`, `WORKER_CONCURRENCY` | the default check interval and concurrent checks (changeable in the interface too) |
| `WORKER_REPLICAS` | how many `worker` processes run on this machine (default 1), see [Operations](/en/admin/operations) |

## The first start

1. `docker compose up -d --build` — at start the worker runs the database
   migrations and sets up the initial data by itself.
2. **The operator's first sign-in**: until they have a password or a Microsoft
   or Google binding, a one-time link is in the worker's log
   (`docker compose logs worker | grep set-password`). It is valid for 7 days
   and renewed at every start.
3. On the **Installation** page, set the default API keys and the default
   outgoing mail (for invitations).
4. On the **Organisations** page, set up an organisation or send an invitation
   to found one — see [Installation administration](/en/admin/installation-admin).

## Publishing

Nothing on the server needs to listen on the internet: a Cloudflare Tunnel
(`docker compose --profile tunnel up -d`) routes the public address to the
service `http://web:80`. Cloudflare Access in front of the address is a
sensible second gate. Set `WEB_PUBLIC_URL` to the public address and add it as
a redirect URI at the Microsoft and Google clients.

## Next

- [Installation administration](/en/admin/installation-admin) — organisations,
  default keys and sender,
- [Operations and capacity](/en/admin/operations) — how many workers and
  how to tell,
- [Backup and restore](/en/admin/backup-and-restore),
- [Updating draften](/en/admin/updating),
- [Troubleshooting](/en/admin/troubleshooting).
