How to Configure Kaneo: Complete Environment Variable Guide for Self-Hosted Deployment

Kaneo uses a single .env file at the repository root to configure all API, web, and WebSocket services through environment variables consumed by the monorepo's services.

Kaneo is an open-source project management platform built as a monorepo. Understanding how to configure Kaneo properly requires knowing which environment variables control each service—authentication, database connections, CORS, and third-party OAuth providers—and how the codebase consumes these values at runtime.

This guide covers every required and optional variable, their implementation in source files, and practical examples for local development and production deployments.

Core Configuration Architecture

Configuration in Kaneo follows a centralized pattern. The .env file at the repository root feeds all services through process.env access in the API and build-time injection in the web client.

Key configuration files include:

Required Environment Variables

These variables must always be set for Kaneo to start successfully.

Variable Purpose Example Value
AUTH_SECRET JWT signing secret (minimum 32 characters for Better Auth) your-super-secret-32-char-key-here
DATABASE_URL PostgreSQL connection string for Drizzle ORM postgresql://kaneo:password@localhost:5432/kaneo
KANEO_API_URL Absolute URL serving the API (used by web client and redirects) http://localhost:1337
KANEO_CLIENT_URL Absolute URL serving the web frontend (CORS and redirect target) http://localhost:5173

Authentication Secret Implementation

The AUTH_SECRET is consumed by Better Auth middleware throughout the API. The value must be cryptographically strong in production.

// apps/api/src/mcp/oauth.ts
const auth = betterAuth({
  secret: process.env.AUTH_SECRET!,
  // ... additional configuration
});

Database Connection Setup

Drizzle ORM initializes its PostgreSQL connection using DATABASE_URL directly.

// apps/api/src/database/client.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

export const db = drizzle(pool);

Optional Configuration Variables

These variables enable additional features or override defaults.

Variable Purpose When Required
CORS_ORIGINS Comma-separated allowed origins; empty string allows all (development only) Production with strict CORS
VITE_API_URL Build-time override for API endpoint in web client Custom Vite dev server setup
REDIS_URL Redis connection for multi-instance WebSocket broadcasting Horizontal API scaling

CORS Origin Configuration

The CORS_ORIGINS variable controls cross-origin resource sharing. Leave unset in development for permissive defaults; set explicitly in production.

// Internal middleware usage pattern
const corsOrigins = process.env.CORS_ORIGINS?.split(",") ?? "*";

Redis-Backed WebSocket Scaling

When REDIS_URL is present, Kaneo switches from in-memory to Redis pub/sub for WebSocket message broadcasting across multiple API instances.

// apps/api/src/ws/index.ts
import { createBroadcastAdapter } from "./broadcast-adapter";

export const broadcast = process.env.REDIS_URL
  ? createBroadcastAdapter("redis", process.env.REDIS_URL)
  : createBroadcastAdapter("in-memory");

The Redis adapter implementation lives in [redis-broadcast-adapter.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/ws/redis-broadcast-adapter.ts).

SSO Provider Configuration

Kaneo supports multiple OAuth providers for authentication. Each requires its own credential pair.

Variable Pattern Provider File Reference
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET GitHub OAuth [oauth.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/mcp/oauth.ts)
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET Google OAuth [oauth.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/mcp/oauth.ts)
DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET Discord OAuth [oauth.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/mcp/oauth.ts)

Provider configuration follows a consistent pattern in the OAuth module:

// apps/api/src/mcp/oauth.ts
providers: [
  process.env.GITHUB_CLIENT_ID && {
    name: "github",
    clientId: process.env.GITHUB_CLIENT_ID,
    clientSecret: process.env.GITHUB_CLIENT_SECRET,
  },
  // Additional providers follow same structure
].filter(Boolean),

Frontend API Resolution

The web client resolves its API endpoint through a utility that checks multiple sources in priority order.

// apps/web/src/lib/utils/api-url.ts
import { env } from "@/lib/env";

export const API_URL = env.VITE_API_URL ?? process.env.KANEO_API_URL!;

This feeds into the TanStack Query client initialization:

// apps/web/src/query-client/index.ts
import { API_URL } from "@/lib/utils/api-url";
import { QueryClient } from "@tanstack/react-query";

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryFn: async ({ queryKey }) => {
        const res = await fetch(`${API_URL}${queryKey[0]}`);
        return res.json();
      },
    },
  },
});

Complete .env Example

Based on the .env.sample template, here is a production-ready configuration:


# Core URLs

KANEO_API_URL=https://api.kaneo.example.com
KANEO_CLIENT_URL=https://app.kaneo.example.com

# Security

AUTH_SECRET=your-production-secret-min-32-chars-long

# Database

DATABASE_URL=postgresql://kaneo:securepassword@db.example.com:5432/kaneo?sslmode=require

# CORS (production)

CORS_ORIGINS=https://app.kaneo.example.com

# Optional: Redis for scaling

REDIS_URL=redis://redis.example.com:6379

# Optional: SSO providers

GITHUB_CLIENT_ID=gh_client_xxx
GITHUB_CLIENT_SECRET=gh_secret_xxx
GOOGLE_CLIENT_ID=google_client_xxx
GOOGLE_CLIENT_SECRET=google_secret_xxx

Configuration Deployment Methods

Local Development

Copy .env.sample to .env, fill values, then run:

pnpm install
pnpm dev

Docker Compose

Environment variables pass through docker-compose.yml or an external .env file referenced with env_file:

services:
  api:
    env_file: .env
    environment:
      - NODE_ENV=production

Kubernetes

Use Secrets for sensitive values and ConfigMaps for non-sensitive configuration, mounting both as environment variables in pod specifications.

Summary

  • Kaneo configuration is centralized in a single .env file at the repository root
  • Four variables are always required: AUTH_SECRET, DATABASE_URL, KANEO_API_URL, KANEO_CLIENT_URL
  • Redis via REDIS_URL enables horizontal scaling of WebSocket broadcasts
  • SSO providers use consistent *_CLIENT_ID and *_CLIENT_SECRET patterns in oauth.ts
  • The web client resolves API endpoints through api-url.ts with fallback to KANEO_API_URL

Frequently Asked Questions

What happens if I don't set CORS_ORIGINS?

Leaving CORS_ORIGINS empty configures permissive CORS suitable for development. In production, this exposes your API to cross-origin requests from any domain. Set explicit origins matching your deployed web client URL.

How long should my AUTH_SECRET be?

Kaneo requires a minimum of 32 characters for AUTH_SECRET to satisfy Better Auth's security requirements. Generate cryptographically random secrets using openssl rand -hex 32 or similar tooling—never commit secrets to version control.

Can I run Kaneo without Redis?

Yes. Redis is strictly optional. Without REDIS_URL, Kaneo uses an in-memory broadcast adapter that works perfectly for single-instance deployments. Add Redis only when running multiple API replicas behind a load balancer.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →