# How to Manage Kaneo Configurations: A Complete Guide to Environment Variables and API-First Settings

> Master Kaneo configurations with this guide. Learn to manage environment variables and API-first settings for your usekaneo/kaneo repository. Optimize your application's runtime behavior today.

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

---

**Kaneo configurations are managed through environment variables that are read at runtime, exposed via a public `/config` API endpoint, and consumed reactively by the frontend through TanStack Query hooks.**

Kaneo is an open-source project management platform built with a modern stack. Understanding how to manage Kaneo configurations is essential for self-hosting, customizing integrations, and extending the platform. This guide walks through the complete configuration lifecycle—from environment variables to React components—based on the actual source code in the `usekaneo/kaneo` repository.

## How Kaneo's Configuration System Works

Kaneo uses a thin, API-first configuration layer rather than static config files. This design choice enables runtime flexibility and seamless frontend reactivity without rebuilds.

### The Three-Layer Architecture

1. **Environment layer** – `process.env` variables serve as the single source of truth
2. **API layer** – `GET /config` endpoint exposes typed settings as JSON
3. **Client layer** – TanStack Query hooks provide cached, reactive access

Each layer is intentionally simple. The `getSettings()` helper in [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts) performs the only transformation: parsing environment strings into typed values.

## Reading Configuration Settings on the Server

All server-side configuration flows through one function.

### The getSettings() Helper

In [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts), the `getSettings()` function maps environment variables to a typed object:

```typescript
function getSettings() {
  return {
    isDemoMode: process.env.DEMO_MODE === "true",
    hasSmtp: isSmtpConfigured(),
    hasGithubSignIn: !!process.env.GITHUB_CLIENT_ID,
    hasGoogleSignIn: !!process.env.GOOGLE_CLIENT_ID,
    // ... additional flags
  };
}

```

**Key characteristics:**
- Returns a plain JavaScript object with boolean and string properties
- Performs no caching—reads fresh `process.env` values on every call
- Delegates complex logic (like SMTP validation) to specialized utilities

### SMTP-Specific Configuration

Email settings receive special handling in [`packages/email/src/smtp-config.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/email/src/smtp-config.ts). The `getSmtpTransportOptions()` function constructs a complete Nodemailer configuration object, while `isSmtpConfigured()` performs a quick check for required variables.

## Exposing Configurations via the API

The `GET /config` endpoint in [`apps/api/src/config/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/config/index.ts) is the bridge between environment variables and the frontend.

### The Config Route Implementation

```typescript
import { configSchema } from "@/schemas";
import { getSettings } from "@/utils/get-settings";

app.get("/config", (c) => {
  const settings = getSettings();
  return c.json(settings);
});

```

**OpenAPI integration** – The response shape is governed by `configSchema` in [`apps/api/src/schemas.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/schemas.ts), defined with Valibot validators:

```typescript
export const configSchema = v.object({
  isDemoMode: v.boolean(),
  hasSmtp: v.boolean(),
  hasGithubSignIn: v.boolean(),
  hasGoogleSignIn: v.boolean(),
  // ... additional fields
});

```

Thanks to `hono-openapi` decorators, this schema automatically generates documentation and runtime validation without manual synchronization.

## Consuming Kaneo Configurations in the Frontend

The frontend accesses configurations through a typed API client and reactive hooks.

### Fetching Configurations with the API Client

The base URL resolution happens in [`packages/libs/src/api-url.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/api-url.ts) via `resolveApiBaseUrl()`, which handles both development and production environments. The fetcher in [`apps/web/src/fetchers/config/get-config.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/fetchers/config/get-config.ts) wraps the Hono client:

```typescript
import { client } from "@kaneo/libs";

export async function getConfig() {
  const response = await client.config.$get();
  
  if (!response.ok) {
    throw new Error("Failed to fetch config");
  }
  
  return response.json();
}

```

### Using the useGetConfig React Hook

For reactive access, `useGetConfig` in [`apps/web/src/hooks/queries/config/use-get-config.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/hooks/queries/config/use-get-config.ts) wraps the fetcher in TanStack Query:

```tsx
import useGetConfig from "@/hooks/queries/config/use-get-config";

export default function ConfigDisplay() {
  const { data: cfg, isLoading, error } = useGetConfig();

  if (isLoading) return <p>Loading…</p>;
  if (error) return <p>Error loading config</p>;

  return (
    <ul>
      <li>Demo mode: {cfg?.isDemoMode ? "Yes" : "No"}</li>
      <li>SMTP configured: {cfg?.hasSmtp ? "Yes" : "No"}</li>
      <li>GitHub sign‑in: {cfg?.hasGithubSignIn ? "Enabled" : "Disabled"}</li>
    </ul>
  );
}

```

This pattern provides automatic caching, background refetching, and error handling without additional boilerplate.

## Adding a New Kaneo Configuration Flag

Extending the configuration system requires coordinated changes across three files.

### Step 1: Add the Environment Variable

Document the new variable in `.env.example`:

```bash

# Enable real-time chat features

ENABLE_CHAT="true"

```

### Step 2: Expose in getSettings()

Update [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts):

```typescript
function getSettings() {
  return {
    // …existing flags
    enableChat: process.env.ENABLE_CHAT === "true",
  };
}

```

### Step 3: Extend the OpenAPI Schema

Update [`apps/api/src/schemas.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/schemas.ts):

```typescript
export const configSchema = v.object({
  // …existing fields
  enableChat: v.boolean(),
});

```

The frontend automatically receives the new property through `getConfig()` and `useGetConfig()` without additional changes.

## Integration-Specific Configuration Patterns

Kaneo uses consistent patterns for third-party integrations.

### Authentication Providers

Each OAuth provider exposes a boolean flag indicating configuration status:

- `hasGithubSignIn` – checks for `GITHUB_CLIENT_ID`
- `hasGoogleSignIn` – checks for `GOOGLE_CLIENT_ID`
- `hasDiscordSignIn` – checks for `DISCORD_CLIENT_ID`

These flags enable conditional UI rendering (show/hide sign-in buttons) without exposing sensitive credentials.

### Email Configuration

SMTP settings are the most complex configuration area. The [`packages/email/src/smtp-config.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/email/src/smtp-config.ts) module provides:

- `isSmtpConfigured()` – quick boolean check for UI purposes
- `getSmtpTransportOptions()` – full Nodemailer configuration for sending

This separation allows the API to report email capability without constructing transport objects unnecessarily.

## Summary

- **Single source of truth** – All Kaneo configurations originate from environment variables read via `process.env`
- **Server entry point** – The `getSettings()` function in [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts) parses and types all environment variables
- **Public API** – The `GET /config` endpoint in [`apps/api/src/config/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/config/index.ts) exposes settings with automatic OpenAPI documentation
- **Frontend access** – Use `useGetConfig()` for reactive state or `getConfig()` for imperative fetching
- **Extensibility** – Adding flags requires updating three files: environment documentation, `getSettings()`, and `configSchema`

## Frequently Asked Questions

### Where does Kaneo store its configuration files?

Kaneo does not use static configuration files. All settings are read from environment variables at runtime through `process.env`, making it ideal for containerized deployments and secret management systems.

### How do I add a custom configuration option to Kaneo?

Add your variable to `.env.example`, parse it in [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts), and extend `configSchema` in [`apps/api/src/schemas.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/schemas.ts). The frontend will automatically receive the new value through existing hooks.

### Why does Kaneo expose configurations through an API endpoint instead of baking them into the build?

The API-first approach enables runtime configuration changes without recompiling the frontend. This is critical for Docker deployments where the same image runs across different environments (staging, production) with different settings.

### How can I check if email notifications are configured in Kaneo?

Query the public config endpoint or use `useGetConfig()` in a React component. The `hasSmtp` boolean indicates whether SMTP credentials are present, while [`packages/email/src/smtp-config.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/email/src/smtp-config.ts) contains the detailed transport configuration used when sending actual emails.