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

# Configuration

> Required and optional environment settings.

export const Screenshot = ({light, dark, alt, caption, width, height, sizes, portrait = false}) => <figure className={portrait ? "oss-doc-screenshot oss-doc-screenshot-portrait" : "oss-doc-screenshot"}>
    <div className="oss-product-light">
      <img src={light} alt={alt} width={width} height={height} sizes={sizes} loading="lazy" />
    </div>
    <div className="oss-product-dark">
      <img src={dark || light} alt={alt} width={width} height={height} sizes={sizes} loading="lazy" />
    </div>
    {caption ? <figcaption>{caption}</figcaption> : null}
  </figure>;

The API needs four values. Copy `.env.example` to `.env` for Compose, or supply them as runtime environment variables through your deployment platform:

| Variable | Purpose |
| - | - |
| `DATABASE_URL` | Complete PostgreSQL connection URL for your dedicated Mill database. Include required TLS settings. |
| `MILL_SECRET` | Random secret of at least 32 characters. Preserve it across upgrades and restores. |
| `MILL_WEB_URL` | Exact public UI origin, such as `https://tasks.example.com` or `http://localhost:4322`. |
| `MILL_API_URL` | Exact public API origin, such as `https://tasks-api.example.com` or `http://localhost:4321`. |

Percent-encode special characters in the database URL's username or password. Mill rejects missing or invalid configuration before it serves requests; there is no fallback production database or default login. It applies the migrations packaged in the API image automatically.

For [bundled PostgreSQL](/installation#optional-run-postgresql-with-compose), also set `POSTGRES_PASSWORD` and put that same password in `DATABASE_URL=postgres://mill:<password>@postgres:5432/mill`. An existing or managed database needs no `POSTGRES_PASSWORD` variable.

The UI needs `MILL_API_URL` set to the same public API origin as the API. It emits this value in a no-store runtime script, so one built image can serve different installations. Never give the UI database credentials or `MILL_SECRET`.

Image versions and published ports belong in the Compose file. Both API and UI image references must come from the same release. Runtime internals such as migration paths, production mode and password-work limits are already set by the images and their safe defaults; they are not installation fields.

Environment changes take effect when you recreate the affected service using your installation's Compose command. A container restart alone keeps its old environment. Preserve the original `MILL_SECRET` with encrypted backups of your configuration.

## Public URL

Remote access requires HTTPS. Local HTTP OAuth is permitted automatically only for an exact loopback `MILL_API_URL`; there is no insecure-origin switch. For browser passkeys, use `localhost` locally or an HTTPS DNS hostname remotely. Browsers do not allow passkey registration with an IP address as the relying-party domain.

The UI origin is used for passkeys and account links; the API origin is used for the session cookie, OAuth issuer and MCP resource. Both must be set before people register passkeys or connect clients. A UI-origin change can invalidate passkey origin checks; read [operations](/operations) before changing an established installation's URL.

## Notifications

Mill v1 uses in-app assignment and mention notifications; task email notifications are excluded. Identity and invitation email is optional. Configure `MILL_SMTP_HOST` and `MILL_SMTP_FROM` together to enable verification links, email changes, password reset links, and invitation email verification. `MILL_SMTP_FROM` is a plain email address. `MILL_SMTP_PORT` defaults to 587; `MILL_SMTP_SECURE=false` requires STARTTLS for remote hosts, while `true` uses implicit TLS (usually port 465). Configure `MILL_SMTP_USER` and `MILL_SMTP_PASSWORD` together when authentication is required. Certificate validation stays enabled; only exact loopback SMTP hosts may use a plaintext development fixture.

Without SMTP, administrators share private invitation links and use the local account-recovery procedure described in [getting started](/getting-started). An invitation issued with email verification enabled keeps that requirement if SMTP is later disabled; reissue a private invitation explicitly instead of weakening an existing link. Existing accounts start with unverified email facts; sign-in remains available, and verification is recorded only after actual email proof.

The Node service drains a PostgreSQL outbox every five seconds. Sensitive message bodies, links, and codes are encrypted with an authenticated key derived from `MILL_SECRET`; preserve this secret across restart, backup, and restore. Delivery uses bounded SMTP deadlines, a two-minute lease, and at most five attempts with delayed retries. Account/inviter authority and lease ownership are checked under targeted locks immediately before sending starts; network I/O holds no database locks. Cancellation before initiation prevents sending. Cancellation after initiation cannot recall an email, but invalidates its proof and prevents another attempt. Delivered, cancelled, exhausted, and expired messages lose their encrypted payload. Maintenance still expires payloads when SMTP is disabled. SMTP handoff is asynchronous: a successful request means queued, and a process interruption after SMTP acceptance can cause a duplicate email. All copies retain the same single-use proof.

Verification and email-change links expire after one hour. Invitation codes expire after ten minutes and allow five failed attempts; a successful code produces a ten-minute proof bound to the invitation and submitted name before final account creation. Email changes require recent browser identity proof, including a passkey when configured. Confirmation invalidates sessions and owned API/MCP grants. Password resets preserve passkeys; operator recovery can explicitly remove lost factors. Public resend/reset endpoints return the same acknowledgment for unknown, disabled, verified, and unverified addresses; uniform persistent address/email throttles and a 500 ms response floor limit probing. No account state is returned.

Environment files and full database backups need protected storage. Do not paste their contents into logs or screenshots.

## In the app

### Team settings: General

<Tabs>
  <Tab title="Desktop">
    <Screenshot light="/assets/screenshots/release-v1/team-light.png" dark="/assets/screenshots/release-v1/team-dark.png" alt="Team settings: General in Mill with sample data." width={2560} height={1800} />
  </Tab>

  <Tab title="Mobile">
    <Screenshot light="/assets/screenshots/release-v1/team-mobile-light.png" dark="/assets/screenshots/release-v1/team-mobile-dark.png" alt="Team settings: General in Mill with sample data." width={780} height={1688} portrait />
  </Tab>
</Tabs>


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