What Database Does Kaneo Use and How Is It Accessed?

Kaneo uses PostgreSQL as its database, with connection configuration resolved dynamically from environment variables via resolveDatabaseConnectionString() in apps/api/src/database/resolve-database-url.ts.

The open-source project management platform Kaneo persists all data in a PostgreSQL database, with flexible runtime configuration that supports both direct connection strings and derived credentials. This article examines the database architecture in the usekaneo/kaneo codebase, covering how the PostgreSQL service is provisioned, how connection parameters are resolved, and how the API initializes its database layer on startup.

PostgreSQL Service Configuration

Kaneo ships with a containerized PostgreSQL instance defined in compose.yml. The Docker-Compose configuration provisions a PostgreSQL 16-alpine container with standard defaults:


# compose.yml (excerpt)

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: kaneo
      POSTGRES_PASSWORD: kaneo
      POSTGRES_DB: kaneo
    ports:
      - "5432:5432"

The API service declares an explicit dependency on the postgres service, ensuring proper startup order. This setup is suitable for local development and small deployments, though production environments may substitute an external PostgreSQL instance.

Database URL Resolution Strategy

The Kaneo API resolves database connection parameters through a deterministic, prioritized algorithm implemented in apps/api/src/database/resolve-database-url.ts. The resolveDatabaseConnectionString() function evaluates three configuration sources in sequence:

Priority Source Behavior
1 DATABASE_URL environment variable Used verbatim if present
2 POSTGRES_* environment variables Composed into a URL when any of POSTGRES_PASSWORD, POSTGRES_HOST, or POSTGRES_PORT is set
3 Hard-coded fallback postgresql://localhost:5432/kaneo when no configuration is provided

Connection String Construction from Component Variables

When the POSTGRES_* variable family is used, Kaneo constructs a URL with these defaults:

  • User: kaneo (or POSTGRES_USER)
  • Password: from POSTGRES_PASSWORD
  • Host: postgres (or POSTGRES_HOST)
  • Port: 5432 (or POSTGRES_PORT)
  • Database: kaneo (or POSTGRES_DB)

The derivation logic (lines 21-71 of resolve-database-url.ts) assembles the final string:

// Format: postgresql://<user>:<password>@<host>:<port>/<db>
const url = `postgresql://${config.user}:${config.password}@${config.host}:${config.port}/${config.db}`;

Local Development Fallback

For unconfigured local environments, the module exports a minimal fallback at the top of the file (lines 1-4):

const FALLBACK_URL = "postgresql://localhost:5432/kaneo";

This allows developers to run the API against a local PostgreSQL instance without any environment configuration.

Programmatic Database Access

The resolved configuration is encapsulated in a ResolvedDatabaseConfig interface that includes:

  • url: the complete connection string
  • host, port, database, username: parsed components

This configuration is logged at startup for operational transparency (lines 33-47).

Exported Helper Function

Any module in the codebase can retrieve the connection string via:

import { resolveDatabaseConnectionString } from "@kaneo/api/src/database/resolve-database-url";

const dbUrl = resolveDatabaseConnectionString();
// Example output: "postgresql://kaneo:password@postgres:5432/kaneo"

Usage with node-postgres

For direct query execution or custom scripts, integrate with the pg client:

import pg from "pg";
import { resolveDatabaseConnectionString } from "@kaneo/api/src/database/resolve-database-url";

const client = new pg.Client({
  connectionString: resolveDatabaseConnectionString()
});

await client.connect();
const result = await client.query("SELECT current_database()");
console.log(result.rows[0]);
await client.end();

Database Initialization on Startup

The prepareDatabaseStartup function in apps/api/src/database/prepare-database-startup.ts orchestrates the API's database readiness protocol. This helper executes two sequential operations:

  1. waitForDatabase: Polls the PostgreSQL instance until connectivity is confirmed
  2. runStartupMigrations: Applies pending schema changes

If either step fails, the startup routine emits detailed error diagnostics including the target host, port, and configuration source (lines 31-53). This fails-fast behavior prevents the API from serving requests against an unready or misconfigured database.

Drizzle ORM Integration

Kaneo uses Drizzle ORM for type-safe database operations. The ORM configuration in apps/api/drizzle.config.ts consumes the same resolveDatabaseConnectionString() helper, ensuring consistency between migration tooling and runtime API behavior:

// drizzle.config.ts — ORM schema configuration
import { resolveDatabaseConnectionString } from "./src/database/resolve-database-url";

export default {
  schema: "./src/schema",
  driver: "pg",
  dbCredentials: {
    connectionString: resolveDatabaseConnectionString()
  }
};

Environment Configuration Examples


# .env

DATABASE_URL=postgresql://kaneo:securepassword@db.internal:5432/kaneo_prod

Component Variables (Development Flexibility)


# .env

POSTGRES_USER=kaneo
POSTGRES_PASSWORD=mysecret
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=kaneo

Minimal Local Setup (No Configuration)

Empty environment — automatically falls back to postgresql://localhost:5432/kaneo.

Summary

  • Kaneo uses PostgreSQL as its sole persistent data store, provisioned via Docker-Compose or external instance
  • Database access is configured through resolveDatabaseConnectionString() in apps/api/src/database/resolve-database-url.ts, supporting DATABASE_URL, POSTGRES_* variables, or localhost fallback
  • Startup sequence waits for connectivity and runs migrations before accepting traffic
  • Drizzle ORM shares the same connection resolution logic for schema management
  • All connection parameters are logged for operational debugging and transparency

Frequently Asked Questions

What version of PostgreSQL does Kaneo require?

Kaneo's compose.yml specifies PostgreSQL 16-alpine, though the connection resolution logic is version-agnostic. Any PostgreSQL 12+ instance should function correctly with the Drizzle ORM schema.

Can I use an external PostgreSQL database instead of the container?

Yes. Set DATABASE_URL to a complete connection string pointing to your external instance, or populate the component POSTGRES_* variables. The API has no dependency on the containerized service specifically.

How does Kaneo handle database migrations?

Migrations run automatically during API startup via runStartupMigrations() in prepare-database-startup.ts. This ensures schema consistency before the application begins serving requests. Manual migration control is available through Drizzle Kit commands using apps/api/drizzle.config.ts.

Where can I inspect the active database configuration?

The resolve-database-url.ts module logs the resolved ResolvedDatabaseConfig at startup, including the source of configuration (DATABASE_URL env var, derived from POSTGRES_* variables, or fallback). Check API logs for lines containing the database host, port, and username.

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 →