Docs Start
Self-hosting
For a team, run the Turnkeel server with Postgres and sign-in. The server is configured by environment variables and by nothing else. At start it checks them, and refuses to start when one that is needed is missing or half set, naming every problem in one message. Telemetry is off.
Run it with Docker
The repository holds a Dockerfile, which builds Turnkeel as one image, and a docker compose file. The code opens at launch.
docker build --tag turnkeel .
The image runs as a user that is not root. It needs TURNKEEL_PUBLIC_URL and TURNKEEL_AUTH_SECRET, and DATABASE_URL for Postgres. Without TURNKEEL_PUBLIC_URL it stops and says so. Its health check is /healthz.
The compose file holds Postgres, an S3-compatible object store and a mail catcher, with Turnkeel as the service app behind the profile app:
docker compose --profile app up --build -d --wait
This starts Turnkeel with sign-in at http://localhost:4400, wired to the three. The passwords in the compose file are for development alone; a real install sets its own.
Environment variables
| Variable | What it sets | When it is not set |
|---|---|---|
TURNKEEL_PUBLIC_URL |
The address people and agents use, in mail links, sign-in callbacks and the changelog feed. Setting it turns sign-in on. | Local mode, on the loopback address only |
TURNKEEL_AUTH_SECRET |
Signs sessions and sign-in links. At least 32 characters, for example from openssl rand -base64 32. |
Needed whenever sign-in is on |
TURNKEEL_HOST, TURNKEEL_PORT |
The bind address and the port | 127.0.0.1 and 4400 |
DATABASE_URL |
Postgres, as postgres://… |
PGlite in the data folder |
DATABASE_URL_DIRECT |
A direct Postgres connection for migrations and the event listener, when DATABASE_URL goes through a pooler |
DATABASE_URL |
TURNKEEL_DATA_DIR |
The data folder | ~/.turnkeel |
TURNKEEL_SECRETS_KEY |
Encrypts connector secrets and the assistant's key before they are stored. At least 32 characters. | No connector with secrets can be installed, and no assistant key can be saved |
SMTP_URL, MAIL_FROM |
Outbound mail, as smtp://… or smtps://… |
Links are written to the server log |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
Sign-in with GitHub | Not offered |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
Sign-in with Google | Not offered |
TURNKEEL_OPEN_SIGNUP |
true lets anyone who reaches the server make an account and a workspace |
Accounts come by invitation only |
TURNKEEL_TRUSTED_PROXIES |
Your reverse proxies' addresses or ranges, such as 10.0.0.0/8, so sign-in limits count the real client address |
No proxy is trusted |
S3_ENDPOINT, S3_BUCKET, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY |
Object storage | Files are kept in a folder under the data folder |
TURNKEEL_MAX_FILE_BYTES |
The largest upload | 100 MB |
Pairs are set together: each client id with its secret, SMTP_URL with MAIL_FROM, and the S3 settings all at once. Open Turnkeel at the address in TURNKEEL_PUBLIC_URL; sign-in refuses any other address of the machine.
Sign-in and mail
The sign-in page shows only the methods the server has set up.
| Method | Offered when |
|---|---|
| Sign-in link by mail | Always, once sign-in is on. A link works once, for 10 minutes. |
| GitHub, Google | Their client id and secret are set. Both are built, and not yet verified against the providers. |
| Passkey | The person added one in their account, after a first sign-in another way |
Mail is plain text, and Turnkeel sends two kinds only: sign-in links and invitations. It sends no notification mail. Without SMTP_URL, the recipient and the link are written to the server log, and the sign-in page says "Mail is not configured. The link is in the server log." An install that other people sign in to sets SMTP_URL.
The first Owner and invitations
On an install with no accounts, the first person to sign in becomes the Owner of a new workspace, which they name. Sign in before you give the address to anyone. After that, an account is made only by accepting an invitation, unless TURNKEEL_OPEN_SIGNUP=true.
An Owner or Admin invites by email from the workspace menu, Members. Only an Owner invites an Owner, and nobody invites to a role above their own. An invitation expires after 7 days, works once, can be revoked, and is accepted only by a signed-in person whose verified email is the one invited.
| Role | Can |
|---|---|
| Owner | Everything, including deleting or handing over the workspace |
| Admin | Everything but deleting or handing over the workspace and managing Owners: members, agents and tokens, connectors, the assistant's settings, money settings |
| Member | Create and edit cards, builds, posts, releases and links; pass gates; sync; propose assets and decisions; triage the inbox |
| Reviewer | Decide assets, decisions and posts; write asset notes and status lines |
| Viewer | Read everything in their projects |
| Tester | Only Testing, in the projects they are invited to |
An invitation can give a role in one project only; a Tester invitation always names a project.
A local install stays local: what local mode holds cannot be moved to a server with sign-in, and a server with sign-in does not start on a local-mode database.
File storage
Uploads (screenshots, assets, evidence, doc images, receipts) go to S3-compatible storage when the S3_ variables are set, and otherwise to a folder under the data folder, served by the server.
S3_PUBLIC_ENDPOINTis the store's address as a browser reaches it, when that differs fromS3_ENDPOINT. Signed upload and download addresses are made for it.S3_FORCE_PATH_STYLE=trueis for stores that need path-style addresses.- The bucket needs a CORS rule that lets a browser PUT to it from Turnkeel's address.
The assistant
The assistant is optional. It drafts and summarises text when a person presses a control, such as Catch me up on a card. It is off in every workspace until an Owner or Admin sets it up in the workspace menu, Assistant, and until then the server sends nothing to any model. It has no tools and changes nothing; a person saves what it suggests. No agent can start it. Each workspace has a daily cap of calls, 100 unless an Owner or Admin sets another. The demo workspace has no assistant.
It works with any server that speaks the OpenAI chat API, which includes a model server on the same machine, such as Ollama or LM Studio, with no key. It also has an adapter for one hosted API. On a server with sign-in, the settings page does not take a local address, so whoever runs the server sets an install-wide default in the environment:
TURNKEEL_ASSISTANT_PROVIDER=openai-compatible
TURNKEEL_ASSISTANT_BASE_URL=http://127.0.0.1:11434/v1
TURNKEEL_ASSISTANT_MODEL=<model>
TURNKEEL_ASSISTANT_API_KEY holds a key when the provider needs one, and TURNKEEL_ASSISTANT_DAILY_CAP limits the calls a day that all workspaces make with this default. A workspace uses the default only after an Owner or Admin chooses it and turns the assistant on there. With TURNKEEL_OPEN_SIGNUP=true the default is offered to no workspace.
The compose file has a model server, Ollama, behind the profile assistant. It pulls no model; you pull one by hand.
The source of this page will be on GitHub when the repository opens. Something wrong or unclear? Write to us.