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(orPOSTGRES_USER) - Password: from
POSTGRES_PASSWORD - Host:
postgres(orPOSTGRES_HOST) - Port:
5432(orPOSTGRES_PORT) - Database:
kaneo(orPOSTGRES_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 stringhost,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:
waitForDatabase: Polls the PostgreSQL instance until connectivity is confirmedrunStartupMigrations: 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
Single Variable (Recommended for Production)
# .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()inapps/api/src/database/resolve-database-url.ts, supportingDATABASE_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →