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

> Configure Kaneo for self-hosted deployment using environment variables in the root .env file. Master API, web, and WebSocket settings for a seamless setup.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: how-to-guide
- Published: 2026-08-05

---

**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:

- [`.env.sample`](https://github.com/usekaneo/kaneo/blob/main/.env.sample) — template defining all available variables
- [[`apps/api/src/mcp/oauth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/mcp/oauth.ts)](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/mcp/oauth.ts) — SSO provider credential consumption
- [[`apps/api/src/ws/redis-broadcast-adapter.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/ws/redis-broadcast-adapter.ts)](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/ws/redis-broadcast-adapter.ts) — Redis WebSocket scaling
- [[`apps/web/src/lib/utils/api-url.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/utils/api-url.ts)](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/utils/api-url.ts) — frontend API resolution

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

```typescript
// 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.

```typescript
// 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.

```typescript
// 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.

```typescript
// 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/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/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/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/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:

```typescript
// 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.

```typescript
// 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:

```typescript
// 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`](https://github.com/usekaneo/kaneo/blob/main/.env.sample) template, here is a production-ready configuration:

```bash

# 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:

```bash
pnpm install
pnpm dev

```

**Docker Compose**

Environment variables pass through [`docker-compose.yml`](https://github.com/usekaneo/kaneo/blob/main/docker-compose.yml) or an external `.env` file referenced with `env_file`:

```yaml
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`](https://github.com/usekaneo/kaneo/blob/main/oauth.ts)
- **The web client resolves API endpoints** through [`api-url.ts`](https://github.com/usekaneo/kaneo/blob/main/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.