# How Kaneo Handles Environment Variables for Configuration: A Complete Guide to dotenv-mono

> Learn how Kaneo centralizes configuration using dotenv-mono to load environment variables and expose strongly-typed settings. Master runtime configuration for your applications.

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

---

**Kaneo centralizes runtime configuration by using the `dotenv-mono` package to load environment-specific .env files, exposing a strongly-typed settings object through [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts).**

The open-source project management platform Kaneo (available at `usekaneo/kaneo`) manages secrets and configuration through a systematic approach that separates code from sensitive data. By leveraging the `dotenv-mono` ecosystem, Kaneo ensures consistent configuration access across its API while maintaining type safety and test isolation.

## The Configuration Architecture: dotenv-mono and get-settings.ts

At the heart of Kaneo's configuration system lies the `dotenv-mono` package, which serves as the single source of truth for all environment variables. The entry point [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts) imports the `config` function from `dotenv-mono` and constructs a validated settings object that downstream modules consume.

This centralized approach means no other file in the codebase directly accesses `process.env`. Instead, every module imports the typed configuration object, creating a clear boundary between environment parsing and business logic.

## Loading Order and Environment-Specific Files

**`dotenv-mono`** reads a hierarchy of `.env` files located at the repository root based on the current `NODE_ENV`. The package automatically selects the appropriate file (such as `.env.development`, `.env.production`, or `.env.test`) and merges its values with existing `process.env` entries.

Kaneo leverages this hierarchy to maintain separate configurations for local development, staging, and production without code changes. The loader respects the standard priority: environment-specific files override base `.env` files, while existing system environment variables take precedence over file contents.

## Consuming Configuration Values Across the API

Kaneo consumes environment variables through the exported settings object in four critical subsystems:

### Database Connections

In [`apps/api/src/database/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/index.ts), Kaneo retrieves PostgreSQL credentials including `POSTGRES_URL` and `POSTGRES_DB` from the settings object to establish database connections.

### Authentication Secrets

The [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts) module imports `JWT_SECRET` and `SESSION_COOKIE_NAME` to configure Hono authentication middleware, ensuring session management relies on externally supplied secrets rather than hardcoded values.

### GitHub Integration

The GitHub App implementation at [`apps/api/src/plugins/github/utils/github-app.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/plugins/github/utils/github-app.ts) consumes `GITHUB_APP_ID`, `GITHUB_PRIVATE_KEY`, and webhook secrets to authenticate with GitHub's API.

### S3 Storage Credentials

File storage operations in [`apps/api/src/storage/s3.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/storage/s3.ts) obtain `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, and bucket configuration through the same centralized settings object.

```typescript
// apps/api/src/utils/get-settings.ts
import { config } from "dotenv-mono";

export default function getSettings() {
  const env = config();            // Loads .env files based on NODE_ENV
  return {
    // Database
    dbUrl: env.POSTGRES_URL,
    dbName: env.POSTGRES_DB,

    // Auth
    jwtSecret: env.JWT_SECRET,
    sessionCookie: env.SESSION_COOKIE_NAME,

    // GitHub
    ghAppId: env.GITHUB_APP_ID,
    ghPrivateKey: env.GITHUB_PRIVATE_KEY,

    // S3
    s3KeyId: env.S3_ACCESS_KEY_ID,
    s3Secret: env.S3_SECRET_ACCESS_KEY,
    s3Bucket: env.S3_BUCKET,
  };
}

```

```typescript
// apps/api/src/auth.ts
import getSettings from "../utils/get-settings";

const { jwtSecret, sessionCookie } = getSettings();

// Use the secrets to configure Hono authentication middleware

```

```typescript
// apps/api/src/storage/s3.ts
import getSettings from "../utils/get-settings";

const { s3KeyId, s3Secret, s3Bucket } = getSettings();

// Initialise AWS SDK client with the retrieved credentials

```

## Type Safety and the Settings Object

The [`get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/get-settings.ts) utility transforms raw environment strings into a strongly-typed configuration object. This pattern provides **compile-time safety**, ensuring that TypeScript catches missing or mistyped configuration keys before runtime. Downstream code imports this object rather than accessing `process.env` directly, eliminating string-typo errors and enabling IDE autocomplete for configuration values.

## Testing Isolation and Mocking

Kaneo's integration test suite at [`tests/api-integration/setup.ts`](https://github.com/usekaneo/kaneo/blob/main/tests/api-integration/setup.ts) mocks the `dotenv-mono` loader to prevent accidental reads of local `.env` files during test execution. This isolation ensures deterministic, in-memory configuration that prevents test pollution from developer environment variables or production secrets.

## Getting Started: The .env.example Template

New contributors find all required variables documented in `apps/api/.env.example`. This template lists every configuration key with brief descriptions, allowing developers to copy the file to `.env` (or environment-specific variants) and populate actual secrets without hunting through source code.

## Summary

- Kaneo uses **`dotenv-mono`** to load hierarchical `.env` files based on `NODE_ENV`, supporting environment-specific configurations.
- The **[`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts)** module centralizes all configuration access, returning a strongly-typed settings object.
- Database, authentication, GitHub, and S3 modules consume this object rather than accessing `process.env` directly.
- Type safety prevents runtime configuration errors through compile-time validation.
- Integration tests mock the configuration loader to ensure deterministic, isolated environments.
- **`apps/api/.env.example`** serves as the definitive reference for required environment variables.

## Frequently Asked Questions

### What package does Kaneo use to load environment variables?

Kaneo relies on the **`dotenv-mono`** package to parse and load environment files. This package handles the complexity of selecting the correct `.env` file based on the current `NODE_ENV` and merging values with existing system environment variables.

### How does Kaneo handle different environments like development and production?

Kaneo leverages `dotenv-mono`'s automatic file selection to load `.env.development`, `.env.production`, or `.env.test` depending on the `NODE_ENV` value. This allows the same codebase to run with different configurations by simply changing the environment variable or file presence.

### Where should I define environment variables when self-hosting Kaneo?

Create a `.env` file in the repository root (or environment-specific variants like `.env.production`) using **`apps/api/.env.example`** as your template. The [`get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/get-settings.ts) utility will automatically load these values when the API starts.

### How does Kaneo prevent configuration errors in production?

By exporting a typed settings object from [`apps/api/src/utils/get-settings.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/get-settings.ts), Kaneo ensures that all configuration access happens through a single validated interface. TypeScript will fail compilation if required keys are missing or if code attempts to access undefined configuration properties.