Skip to content

Quickstart

Two containers, and everything else in the browser.

Budget 15–20 minutes, plus DNS propagation. Getting the app running is one command; the four DNS records your support address needs are the rest of it. Nobody sets this up in five minutes and we would rather say so here than have you find out at record three.

Before you start

  • A domain you control. The support address lives on a subdomain of it — help.example.com, never the root. Kiku does not check this at boot; it just cannot work without it.
  • A mailbox on that subdomain, with IMAP and SMTP credentials. Any provider.
  • Docker with the Compose plugin.
  • A reverse proxy terminating TLS. Not optional in practice — see Reverse proxy.

The Compose file

Grab compose.yaml from the repo. It is the whole deployment: the app, Postgres and instance mail configuration.

Set APP_URL to the public HTTPS URL you will reach the app on. It has no default and the app refuses to boot without it, because a guessed base URL breaks deep links and the session cookie silently instead of loudly.

Generate SECRET_KEY with:

Terminal window
openssl rand -hex 32 # put this in SECRET_KEY

The app also needs instance mail at boot. The simplest correct answer is to reuse the support mailbox’s own SMTP credentials: set INSTANCE_MAIL_HOST, INSTANCE_MAIL_USER and INSTANCE_MAIL_PASSWORD, and choose INSTANCE_MAIL_FROM — for example, Kiku <instance@example.com>. Port and TLS use the defaults shown in the configuration reference.

The db service publishes no port, so the throwaway kiku:kiku password never leaves the Compose network. Leave it or change it; it makes no difference from outside.

:1 is the major tag. docker compose pull takes every patch and minor of 1.x and can never drag you across a breaking change unattended.

Start it

Terminal window
docker compose up -d

Migrations run automatically at boot. If one fails the app logs the pending migrations and the Postgres error and exits non-zero rather than serving a half-migrated schema — with restart: unless-stopped that is a visible restart loop, which is the point.

Claim the instance

Open /setup. With zero users, any visitor can create the first admin account — no token in the logs, no password parked in a Compose file forever.

Complete setup before you expose the instance publicly. Between up -d and claiming the account, anyone who can reach the app can take it — and this instance’s admin reads every internal note in the inbox.

/setup returns 404 the moment any user exists, and until then the app logs a loud “setup is unclaimed” warning on every boot.

The wizard then walks: admin account → Settings → Email → connection test → plus-addressing probe → done. Every step past the first is skippable and resumable, because waiting on DNS to propagate is not something you do in one sitting.

Connect the mailbox

That is Email setup: four DNS records on the subdomain, the IMAP and SMTP credentials, and the probe that tells you whether reply threading’s strongest layer will work on your mailbox.

Where to next