# What Database Does Kaneo Use and How Is It Accessed?

> Discover Kaneo's PostgreSQL database and learn how its connection string is dynamically accessed using environment variables. Get the details here.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: internals
- Published: 2026-08-29

---

**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`](https://github.com/usekaneo/kaneo/blob/main/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](https://github.com/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`](https://github.com/usekaneo/kaneo/blob/main/compose.yml)**. The Docker-Compose configuration provisions a PostgreSQL 16-alpine container with standard defaults:

```yaml

# 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`](https://github.com/usekaneo/kaneo/blob/main/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`](https://github.com/usekaneo/kaneo/blob/main/resolve-database-url.ts)) assembles the final string:

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

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

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

```typescript
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`](https://github.com/usekaneo/kaneo/blob/main/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`](https://github.com/usekaneo/kaneo/blob/main/apps/api/drizzle.config.ts)** consumes the same `resolveDatabaseConnectionString()` helper, ensuring consistency between migration tooling and runtime API behavior:

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

### Single Variable (Recommended for Production)

```bash

# .env

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

```

### Component Variables (Development Flexibility)

```bash

# .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`](https://github.com/usekaneo/kaneo/blob/main/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`](https://github.com/usekaneo/kaneo/blob/main/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`](https://github.com/usekaneo/kaneo/blob/main/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`](https://github.com/usekaneo/kaneo/blob/main/apps/api/drizzle.config.ts).

### Where can I inspect the active database configuration?

The [`resolve-database-url.ts`](https://github.com/usekaneo/kaneo/blob/main/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.