How to Deploy a Kaneo Application: 5 Methods for Production and Development
You can deploy Kaneo using drim CLI for automated setup, Docker Compose for local testing, or Helm charts for Kubernetes clusters, with optional external PostgreSQL and Kubernetes Secrets for production security.
Kaneo is an open-source project management platform built with Hono, Drizzle ORM, and Better Auth on the backend, paired with a React frontend. The entire application ships as a single container image from ghcr.io/usekaneo/kaneo:latest, bundling both API and web layers. This unified architecture simplifies deployment across environments, whether you're running a local instance or scaling across a production Kubernetes cluster.
Quick-Start One-Click Deployment with drim
The fastest path to a production-ready Kaneo instance uses the drim CLI, which automates container pulls, HTTPS certificate provisioning, and database initialization.
According to the Kaneo README, run:
curl -fsSL https://assets.kaneo.app/install.sh | sh
drim setup
This method handles SSL termination, reverse proxy configuration, and database creation without manual intervention. It's the recommended approach for teams without existing container orchestration infrastructure.
Docker Compose Deployment for Local Testing
For development environments or small self-hosted deployments, a minimal compose.yml spins up PostgreSQL alongside the Kaneo container. The configuration in [compose.yml](https://github.com/usekaneo/kaneo/blob/main/compose.yml) demonstrates the expected service structure.
First, copy the environment template:
cp .env.sample .env
Edit .env to set POSTGRES_PASSWORD and AUTH_SECRET (minimum 32 characters). Then deploy:
services:
postgres:
image: postgres:16-alpine
env_file: .env
ports: ["5432:5432"]
volumes: ["postgres_data:/var/lib/postgresql/data"]
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kaneo -d kaneo"]
interval: 10s
timeout: 5s
retries: 5
kaneo:
image: ghcr.io/usekaneo/kaneo:latest
ports: ["5173:5173"]
env_file: .env
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
volumes:
postgres_data:
Start with:
docker compose up -d
Access the application at http://localhost:5173. The healthcheck ensures PostgreSQL is ready before Kaneo attempts connections, preventing startup race conditions.
Kubernetes Deployment with Helm Charts
For production clusters, the official Helm chart under charts/kaneo/ provisions Kaneo with configurable ingress, TLS, and resource limits as documented in [charts/kaneo/README.md](https://github.com/usekaneo/kaneo/blob/main/charts/kaneo/README.md).
Basic Installation
Create a namespace and install:
helm install kaneo oci://ghcr.io/usekaneo/charts/kaneo \
--namespace kaneo --create-namespace
kubectl port-forward svc/kaneo-kaneo 5173:5173 -n kaneo
Production with Ingress
Expose via HTTPS using your cluster's ingress controller:
helm install kaneo oci://ghcr.io/usekaneo/charts/kaneo \
--namespace kaneo --create-namespace \
--set ingress.enabled=true \
--set ingress.className=nginx \
--set "ingress.hosts[0].host=mydomain.com"
All parameters are defined in [charts/kaneo/values.yaml](https://github.com/usekaneo/kaneo/blob/main/charts/kaneo/values.yaml). Environment-specific configuration lives under kaneo.env, including authSecret and clientUrl values.
External PostgreSQL Configuration
For scalable production deployments, disable the bundled PostgreSQL and connect to a managed database. Set postgresql.enabled=false and provide connection details via kaneo.env.database.external as shown in the Helm documentation:
kaneo:
env:
authSecret: "secure-32-char-secret"
clientUrl: "https://kaneo.your-domain.com"
database:
external:
enabled: true
host: "db.example.com"
port: 5432
database: "kaneo"
username: "kaneo_user"
password: "strong-password"
This separation allows independent scaling, automated backups, and high-availability configurations managed by your database provider.
Secrets Management with Kubernetes
Production deployments should store sensitive values in Kubernetes Secrets rather than plain values.yaml files. Create a secret with your authentication and database credentials:
kubectl create secret generic kaneo-secrets \
--namespace kaneo \
--from-literal=auth-secret="a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
--from-literal=postgres-password="my$ecureP%ssw0rd"
Reference it in your deployment configuration:
kaneo:
env:
existingSecret:
enabled: true
name: "kaneo-secrets"
key: "auth-secret"
This pattern prevents credential leakage in version control and enables secret rotation without redeploying the application.
Required Environment Variables
Several variables are mandatory for correct operation, as defined in .env.sample:
| Variable | Purpose | Requirement |
|---|---|---|
AUTH_SECRET |
Encryption key for Better Auth sessions | Minimum 32 characters |
KANEO_CLIENT_URL |
Public-facing URL for CORS and redirects | Must match browser address exactly |
POSTGRES_PASSWORD / DATABASE_URL |
Database authentication | Required unless using socket connections |
Critical: If KANEO_CLIENT_URL mismatches the actual browser URL, authentication fails with an "invalid origin" error. The application generates a random AUTH_SECRET at startup only if the variable is absent—explicit configuration is strongly recommended for production stability.
Container Architecture Details
The multi-stage Dockerfile builds both backend and frontend into a single image. The API routes under /api/* are served by Hono, while the React application handles all other paths on port 5173. Optional Redis can be configured for WebSocket pub/sub in multi-replica deployments, though the default in-memory adapter suffices for single-instance setups.
Summary
- drim CLI provides the fastest production deployment with automatic HTTPS and database setup
- Docker Compose works best for local development and small teams, using the sample [
compose.yml](https://github.com/usekaneo/kaneo/blob/main/compose.yml) - Helm charts enable scalable Kubernetes deployments with ingress, resource limits, and external database support
- External PostgreSQL should replace bundled databases for production workloads requiring HA and backups
- Kubernetes Secrets properly isolate sensitive configuration from application code
Frequently Asked Questions
What is the minimum server requirement for running Kaneo?
Kaneo runs comfortably on a single CPU core with 1GB RAM for small teams. The containerized architecture means resource limits are primarily dictated by PostgreSQL performance and concurrent user load. For production, allocate at least 2GB RAM and enable connection pooling for the database.
Why does login fail with "invalid origin" after deployment?
This error occurs when KANEO_CLIENT_URL (or kaneo.env.clientUrl in Helm) doesn't match the URL in your browser address bar. Better Auth validates the origin header for security. Check for trailing slashes, protocol mismatches (http vs https), or port differences—the value must be exact.
Can I split the API and frontend into separate containers?
The official image bundles both layers for operational simplicity. While the source structure separates apps/api and apps/web, you'd need custom build pipelines to containerize them independently. This is only recommended if you have specific caching or scaling requirements that justify the added complexity.
How do I migrate data when switching from bundled to external PostgreSQL?
Use pg_dump from the running container to export, then restore to your external database. Update connection settings, set postgresql.enabled=false, and redeploy. The Drizzle ORM schema in [apps/api/src/database/schema.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts) ensures compatibility across deployment modes.
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 →