Self Host - Getting Started
This guide covers the standard self-hosted Kanera setup using Docker Compose.
Use it when you want to run Kanera on your own server with your own domains, database, uploads, backups, public API, and MCP endpoint.
Requirements
You need:
- A server with Docker and Docker Compose.
- A domain pointing at the server.
- HTTPS in front of Kanera, usually through Caddy, Traefik, nginx, Dokploy, or a cloud load balancer.
- SMTP credentials if you want Kanera to send email.
- A plan for backing up PostgreSQL data and uploaded files.
Planning capacity
Kanera is lightweight at small-team scale. Its compose file does not set hard CPU or memory limits, and the migration service exits after startup. The default stack runs PostgreSQL, Valkey, two API replicas, a worker, the public API, MCP, and nginx.
Use these as starting points, then measure your own concurrent activity, files, integrations, and backup window:
| Typical team | Starting capacity | Local disk | Notes |
|---|---|---|---|
| Personal or lightly used | 1 vCPU, 1 GiB RAM | 10 GiB | Use API_REPLICAS=1 and leave the optional monitoring profile off. |
| 1 to 25 active users | 1-2 vCPU, 2 GiB RAM | 20 GiB | Comfortable for normal board activity, notifications, and light integrations. |
| 26 to 100 active users | 2-4 vCPU, 4 GiB RAM | 40 GiB | Adds headroom for concurrent activity, uploads, API traffic, webhooks, and database cache. |
| More than 100 active users | 4+ vCPU, 8+ GiB RAM | 80+ GiB | Load-test with representative data before launch, then scale API replicas and tune PostgreSQL from observed usage. |
These are planning baselines, not hard user limits or measured capacity guarantees. Concurrent activity matters more than the number of registered accounts. File volume and retention usually drive disk requirements more than Kanera itself. Keep database and uploaded-file backups outside the server's primary disk, and alert on disk growth.
The optional Prometheus, Grafana, Loki, Alloy, and exporter monitoring profile adds several processes. The deployment guide estimates roughly 0.7 to 1.5 GiB of additional memory, depending on log volume and dashboard usage. Budget that separately or run monitoring on another host.
Deployment shape
The standard compose file runs these services:
| Service | Purpose |
|---|---|
postgres | PostgreSQL database with persistent data in kanera_pgdata. |
valkey | Required Valkey instance for realtime fanout, presence, and shared rate limits. |
migrate | One-shot database migration service that runs before app services start. |
api | Main Kanera API and realtime server. |
worker | Single background worker for schedulers, webhook delivery, notifications, cleanup, and realtime outbox fallback. |
public-api | Public integration API. |
mcp | MCP Streamable HTTP server backed by the public API. |
web | Built Kanera web app served by nginx. |
db-backup | Optional encrypted PostgreSQL backup scheduler. |
The browser should connect to the web service. Do not expose the main api service directly; the web nginx container proxies app API and Socket.IO traffic internally.
Expose public-api only if you want external integrations. Expose mcp only if you want remote MCP clients to connect over HTTP.
Recommended domains
Use separate domains for the app, public API, and MCP when you expose all three:
| Domain | Service | Use |
|---|---|---|
kanera.example.com | web | The Kanera web app. |
api.kanera.example.com | public-api | REST API, webhooks, and API reference. |
mcp.kanera.example.com | mcp | Remote MCP endpoint for AI clients. |
The public API and MCP domains are optional. You can start with only the web app and add the others later.
Create the environment file
On the server, copy the example environment file:
cp .env.example .env
Set the required production values:
WEB_ORIGIN=https://kanera.example.com
COOKIE_DOMAIN=kanera.example.com
COOKIE_SECURE=true
KANERA_ENVIRONMENT=production
JWT_SECRET=<openssl rand -hex 32>
MEDIA_SIGNING_SECRET=<openssl rand -hex 32>
SECRETS_ENCRYPTION_KEY=<openssl rand -hex 32>
Keep these secrets stable across redeploys. Changing JWT_SECRET signs users out. Changing MEDIA_SIGNING_SECRET invalidates existing signed media URLs. SECRETS_ENCRYPTION_KEY protects stored integration secrets and should be distinct from JWT_SECRET.
Self-hosted mode is the default:
KANERA_DEPLOYMENT_MODE=self_hosted
Do not set hosted Stripe billing variables unless you are running a Kanera SaaS-style hosted deployment.
Configure email
SMTP is optional, but recommended for invites, notifications, verification, and operational use.
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_PASSWORD=your-password
SMTP_FROM_NAME=Kanera
Leave email verification disabled until outbound mail is confirmed working:
EMAIL_VERIFICATION_ENABLED=false
After email is working, you can set EMAIL_VERIFICATION_ENABLED=true to require verification for signup, invite signup, and email changes.
Configure notifications
Kanera's notification drawer works without extra setup.
For email notifications, configure SMTP first. Users can then manage their own email channel and per-type email preferences from Profile settings -> Notifications.
For browser push notifications:
- Confirm Kanera is served over HTTPS.
- Open Profile settings -> Org as an organisation admin.
- Enable Allow browser push for this organisation.
- Ask users who want push alerts to open Profile settings -> Notifications, enable Allow push notifications, grant browser permission, and choose their per-type push preferences.

Push is device/browser specific. A user who wants alerts on multiple devices must enable push from each browser or installed PWA.
Configure Trello import attachment copying
Trello board JSON imports work without extra setup.
To let users import Trello file attachments into Kanera, configure a Trello API key:
TRELLO_API_KEY=your-trello-api-key
Create the key from Trello's Power-Up admin page at https://trello.com/power-ups/admin. No Power-Up capabilities are required; the Power-Up/app entry is only the Trello container for the API key. Users connect Trello during the import so Kanera can copy attachments for that import.
Publish ports
The default compose file keeps services private. If you are running Docker directly on a server, add a compose.override.yml file:
services:
web:
ports:
- "8080:80"
public-api:
ports:
- "3001:3001"
mcp:
ports:
- "3002:3002"
Then point your HTTPS reverse proxy at:
| Public URL | Upstream |
|---|---|
https://kanera.example.com | http://127.0.0.1:8080 |
https://api.kanera.example.com | http://127.0.0.1:3001 |
https://mcp.kanera.example.com | http://127.0.0.1:3002 |
Only publish public-api and mcp when you need them.
Trust proxy settings
Set these when Kanera is behind a trusted reverse proxy such as nginx, Traefik, Dokploy, or an ingress that sends the real client IP:
API_TRUST_PROXY=true
PUBLIC_API_TRUST_PROXY=true
API_TRUST_PROXY helps app authentication rate limits use the real client IP. PUBLIC_API_TRUST_PROXY does the same for public API rate limits.
Leave them false only when the service is directly internet-facing or directly Cloudflare-facing.
Start Kanera
Build and start the deployment:
docker compose up -d --build
The migration service runs pending database migrations once before api, worker, and public-api start. The MCP service starts after the public API health check passes.
Check the deployment
Open your web domain in a browser:
https://kanera.example.com

Useful checks:
docker compose ps
docker compose logs -f api
docker compose logs -f worker
docker compose logs -f public-api
docker compose logs -f mcp
Health checks:
curl https://kanera.example.com/api/health
curl https://api.kanera.example.com/health
curl https://mcp.kanera.example.com/health
The public API and MCP checks only apply if those services are exposed.
Public API
The public API service is used for integrations, webhooks, and API docs.
When exposed, the API reference is available at:
| Path | Use |
|---|---|
/docs | Interactive Scalar reference. |
/swagger | Swagger UI reference. |
/openapi.json | OpenAPI document for SDK generation. |
For example:
https://api.kanera.example.com/docs
See API for API keys, first requests, webhooks, and signature verification.

MCP
The MCP service lets AI clients connect to Kanera over Streamable HTTP.
When exposed, the endpoint is:
https://mcp.kanera.example.com/mcp
Set the public MCP URL when exposing it:
MCP_SERVER_PUBLIC_URL=https://mcp.kanera.example.com/mcp
In Docker Compose, keep the internal MCP-to-public-API URL as:
KANERA_PUBLIC_API_URL=http://public-api:3001
MCP clients authenticate with Kanera API keys using:
Authorization: Bearer kanera_live_...
Use a personal key from Profile settings -> API keys for a user's own AI client across their accessible boards. Use a workspace key from Workspace settings -> API for shared MCP clients or server-side automations pinned to one workspace.
OAuth for AI agents
To let compatible agents connect through a browser sign-in instead of an API key, tell Kanera its public OAuth URLs. Set the public API issuer on the public-api service:
PUBLIC_API_OAUTH_ISSUER=https://api.kanera.example.com
MCP_SERVER_PUBLIC_URL (set above) drives the MCP protected-resource metadata. With the bundled docker-compose.yml, two more variables are derived automatically: MCP_PUBLIC_URL (the MCP address shown to users) from MCP_SERVER_PUBLIC_URL, and the MCP service's OAUTH_ISSUER_URL from PUBLIC_API_OAUTH_ISSUER.
Route the OAuth discovery paths to the right service at your reverse proxy:
| Path | Service |
|---|---|
/.well-known/oauth-authorization-server, /oauth/* | public-api |
/.well-known/oauth-protected-resource, /mcp | mcp |
Self-hosted deployments have no plan restriction on agent connections. See Connect an AI agent for the full flow.
See Connect Kanera to your AI agent for client setup and MCP capabilities.

Upload storage
By default, uploaded files are stored in the kanera_uploads Docker volume.
For S3-compatible storage, set:
S3_REGION=auto
S3_BUCKET=kanera
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...
S3_ENDPOINT=https://s3.example.com
S3_PUBLIC_URL_PREFIX=https://cdn.example.com/kanera
When the required S3 values are set, S3 takes precedence over local upload storage.
Backups
Back up both:
| Data | Location |
|---|---|
| PostgreSQL data | kanera_pgdata Docker volume. |
| Uploaded files | kanera_uploads Docker volume, unless S3 storage is configured. |
Manual PostgreSQL backup:
docker compose exec -T postgres pg_dump -U kanera kanera | gzip > kanera-$(date +%F).sql.gz
Kanera can also run encrypted full PostgreSQL backups to S3-compatible storage:
DB_BACKUPS_ENABLED=true
DB_BACKUP_ENCRYPTION_PASSPHRASE=<openssl rand -hex 32>
DB_BACKUP_TIMES_UTC=00:15,12:15,16:45
DB_BACKUP_RETENTION_DAYS=14
DB_BACKUP_S3_PREFIX=backups/postgres
The backup service compresses the dump, encrypts it with GPG symmetric AES-256, uploads it to S3, and prunes old backups after successful uploads. Store DB_BACKUP_ENCRYPTION_PASSPHRASE somewhere durable outside the deployment too; it is required to restore backups.
Updates
For a normal Docker deployment:
git pull
docker compose up -d --build api public-api mcp web
If a release includes database changes, the migration service runs before app services serve traffic.
For Dokploy, redeploy the application after pushing or pulling the latest code. Dokploy rebuilds from docker-compose.yml.
Dokploy notes
Dokploy is a good fit for Kanera because it can run the Docker Compose application and terminate HTTPS through Traefik.
In Dokploy:
- Create a project and application.
- Choose Docker Compose.
- Connect the Kanera repository.
- Set the compose file path to
docker-compose.yml. - Add the production environment variables.
- Deploy once.
- Add domains for
web,public-api, andmcpas needed.
Use these domain routes:
| Domain | Service | Container port |
|---|---|---|
kanera.example.com | web | 80 |
api.kanera.example.com | public-api | 3001 |
mcp.kanera.example.com | mcp | 3002 |
Do not create a public domain for the main api service.
Scaling notes
The default deployment uses multiple app API replicas and one worker.
API_REPLICAScontrols app API process count. Defaults to2.PUBLIC_API_REPLICAScontrols public API process count. Defaults to1.MCP_REPLICAScontrols MCP process count. Defaults to1.- Keep
workerat exactly1. - Valkey is required for realtime fanout, presence, and shared rate limits.
Raise replicas only when the server has enough CPU, memory, and database connection capacity. If you raise API_REPLICAS, review PG_POOL_MAX and PostgreSQL max_connections.
Common issues
| Problem | What to check |
|---|---|
| Users are signed out after login. | Confirm COOKIE_SECURE=true, COOKIE_DOMAIN, and WEB_ORIGIN match the public HTTPS domain. |
/api/health fails on the web domain. | Confirm the domain routes to the web service on port 80, not directly to api. |
| Public API does not respond. | Confirm the domain routes to public-api on port 3001. |
| MCP does not respond. | Confirm the domain routes to mcp on port 3002, MCP_SERVER_PUBLIC_URL points at the public /mcp URL, and KANERA_PUBLIC_API_URL is http://public-api:3001 inside compose. |
| Rate limits affect everyone at once. | Confirm API_TRUST_PROXY=true and PUBLIC_API_TRUST_PROXY=true when behind a trusted proxy. |
| Email verification blocks users. | Leave EMAIL_VERIFICATION_ENABLED=false until SMTP is working. |
| Uploads disappear after redeploy. | Confirm kanera_uploads is persistent or configure S3-compatible storage. |