Skip to main content

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.

Deployment shape

The standard compose file runs these services:

ServicePurpose
postgresPostgreSQL database with persistent data in kanera_pgdata.
valkeyRequired Valkey instance for realtime fanout, presence, and shared rate limits.
migrateOne-shot database migration service that runs before app services start.
apiMain Kanera API and realtime server.
workerSingle background worker for schedulers, webhook delivery, notifications, cleanup, and realtime outbox fallback.
public-apiPublic integration API.
mcpMCP Streamable HTTP server backed by the public API.
webBuilt Kanera web app served by nginx.
db-backupOptional 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.

Use separate domains for the app, public API, and MCP when you expose all three:

DomainServiceUse
kanera.example.comwebThe Kanera web app.
api.kanera.example.compublic-apiREST API, webhooks, and API reference.
mcp.kanera.example.commcpRemote 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.

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 URLUpstream
https://kanera.example.comhttp://127.0.0.1:8080
https://api.kanera.example.comhttp://127.0.0.1:3001
https://mcp.kanera.example.comhttp://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:

PathUse
/docsInteractive Scalar reference.
/swaggerSwagger UI reference.
/openapi.jsonOpenAPI 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 workspace API keys using:

Authorization: Bearer kanera_live_...

See AI MCP 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:

DataLocation
PostgreSQL datakanera_pgdata Docker volume.
Uploaded fileskanera_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:

  1. Create a project and application.
  2. Choose Docker Compose.
  3. Connect the Kanera repository.
  4. Set the compose file path to docker-compose.yml.
  5. Add the production environment variables.
  6. Deploy once.
  7. Add domains for web, public-api, and mcp as needed.

Use these domain routes:

DomainServiceContainer port
kanera.example.comweb80
api.kanera.example.compublic-api3001
mcp.kanera.example.commcp3002

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_REPLICAS controls app API process count. Defaults to 2.
  • PUBLIC_API_REPLICAS controls public API process count. Defaults to 1.
  • MCP_REPLICAS controls MCP process count. Defaults to 1.
  • Keep worker at exactly 1.
  • 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

ProblemWhat 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.