# How to Use Kaneo with Docker: Complete Setup Guide

> Learn how to use Kaneo with Docker in this complete setup guide. Easily deploy Kaneo using Docker Compose for production or local development. Get started today!

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

---

**TLDR:** Kaneo runs as a multi-stage Docker container with separate build stages for the API and web frontend, orchestrated via Docker Compose with PostgreSQL. Use [`compose.yml`](https://github.com/usekaneo/kaneo/blob/main/compose.yml) for production or [`compose.local.yml`](https://github.com/usekaneo/kaneo/blob/main/compose.local.yml) to build from source.

Kaneo is an open-source project management platform with first-class Docker support. This guide walks through exactly how to deploy Kaneo using Docker, whether you need a quick production setup or a local development environment. The repository provides production-ready Docker Compose configurations and a secure, slim runtime image.

## Quick Start with Pre-Built Docker Image

The fastest way to run Kaneo uses the official image from GitHub Container Registry. This approach requires no local build and pulls a tested, production-ready container.

### Configure Environment Variables

Kaneo requires several environment variables defined in `.env.sample`. Copy this file and customize:

```bash
cp .env.sample .env

```

Edit `.env` to set at minimum:

```bash
POSTGRES_PASSWORD=your_secure_password
AUTH_SECRET=$(openssl rand -hex 32)

```

The `AUTH_SECRET` is used for session encryption and must be 32+ characters.

### Launch with Docker Compose

Run the pre-built stack:

```bash
docker compose -f compose.yml up -d

```

This starts two services:
- **PostgreSQL** container with persistent volume
- **Kaneo** application container on port `5173`

Verify the API health endpoint:

```bash
curl http://localhost:5173/api/health

# Expected: {"status":"ok"}

```

Access the web UI at `http://localhost:5173`.

## Building Kaneo from Source with Docker

For development or customization, use [`compose.local.yml`](https://github.com/usekaneo/kaneo/blob/main/compose.local.yml) to build containers directly from the repository source.

### Local Build Workflow

```bash

# 1. Prepare environment

cp .env.sample .env

# Edit with your credentials

# 2. Build and start all services

docker compose -f compose.local.yml up -d --build

```

The `--build` flag forces fresh image construction. Subsequent runs can omit it unless dependencies change.

### How the Local Build Works

The [`compose.local.yml`](https://github.com/usekaneo/kaneo/blob/main/compose.local.yml) orchestrates three services:
- **postgres** — standard PostgreSQL container
- **api** — built from `apps/api/Dockerfile`
- **web** — built from `apps/web/Dockerfile`

Each service connects through Docker's internal network, with the web container proxying API requests as configured.

## Understanding Kaneo's Multi-Stage Dockerfile

The production image uses `Dockerfile.kaneo` with three optimized stages that minimize final image size and attack surface.

### Stage 1: API Builder

```dockerfile

# FROM Dockerfile.kaneo

# api-builder stage

```

This stage:
1. Installs `pnpm` in a `node:20-alpine` base
2. Copies workspace manifests for dependency caching
3. Installs dependencies with `pnpm install`
4. Builds the `email` and `permissions` packages
5. Compiles the API from `apps/api` to `dist/`

Dependency files are copied separately from source code, enabling Docker layer caching. Rebuilds only re-install dependencies when [`package.json`](https://github.com/usekaneo/kaneo/blob/main/package.json) changes.

### Stage 2: Web Builder

Identical pattern for the frontend:
1. Installs dependencies
2. Builds `apps/web` to `dist/`

Each builder stage operates independently, allowing parallel execution.

### Stage 3: Runtime

The final stage creates a minimal, secure production container:

```dockerfile

# FROM Dockerfile.kaneo

# runtime stage

```

Key security and optimization features:

| Feature | Implementation |
|---------|---------------|
| Base image | `node:20-alpine` (minimal attack surface) |
| Web server | nginx for static asset delivery |
| User privilege | Unprivileged `appuser` (non-root) |
| Dependencies | `pnpm install --prod` only (no dev dependencies) |
| Health check | Built-in container health verification |

The runtime copies only compiled artifacts from builder stages—no source code, build tools, or development dependencies remain in the final image.

### Entrypoint Script

The container launches via [`kaneo-entrypoint.sh`](https://github.com/usekaneo/kaneo/blob/main/kaneo-entrypoint.sh), which:
- Injects runtime environment variables into nginx configuration
- Starts the API server and nginx

This pattern allows the same image to run across environments with different configurations.

## Docker Compose Configuration Reference

### Production: compose.yml

```yaml

# From compose.yml

services:
  postgres:
    image: postgres:16-alpine
    # Persistent volume for data

    
  kaneo:
    image: ghcr.io/usekaneo/kaneo:latest
    ports:
      - "5173:5173"
    environment:
      # Injected from .env

    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5173/api/health"]

```

The health check automatically restarts unhealthy containers.

### Development: compose.local.yml

```yaml

# From compose.local.yml

services:
  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    
  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile

```

Local builds use distinct Dockerfiles for each service, enabling independent iteration on API versus frontend.

## Environment Variables and Configuration

The `.env.sample` file documents all supported variables:

| Variable | Required | Purpose |
|----------|----------|---------|
| `POSTGRES_PASSWORD` | Yes | Database authentication |
| `POSTGRES_USER` | No | Default: `postgres` |
| `POSTGRES_DB` | No | Default: `kaneo` |
| `AUTH_SECRET` | Yes | Session encryption key |
| `DATABASE_URL` | Auto-generated | Override connection string |
| `REDIS_URL` | No | Optional external Redis |

Database URL format when overriding manually:

```

postgresql://user:password@host:port/database

```

## Security Best Practices

Kaneo's Docker implementation follows container security fundamentals:

- **Non-root execution:** The `appuser` account runs all processes
- **Minimal runtime:** No build tools, compilers, or source code in production
- **Health verification:** Automated restart on service failure
- **Layer caching:** Dependency installation separated from source changes

The multi-stage build ensures the runtime image contains only what's necessary to execute—approximately the `node:20-alpine` base plus nginx, compiled JavaScript, and production npm packages.

## Troubleshooting Docker Deployments

### Container fails to start

Check logs for missing environment variables:

```bash
docker compose logs kaneo

```

Common causes: undefined `AUTH_SECRET` or invalid database credentials.

### Database connection errors

Verify PostgreSQL container health:

```bash
docker compose ps

# Status should show "healthy"

```

Test connectivity from the Kaneo container:

```bash
docker compose exec kaneo wget -qO- http://localhost:5173/api/health

```

### Build cache issues

Force clean rebuild:

```bash
docker compose -f compose.local.yml build --no-cache

```

## Summary

- **For production:** Use `docker compose -f compose.yml up -d` with the pre-built image from GitHub Container Registry
- **For development:** Use `docker compose -f compose.local.yml up -d --build` to compile from source
- **The `Dockerfile.kaneo`** implements a three-stage build (api-builder, web-builder, runtime) for optimized, secure containers
- **Minimum required configuration:** `POSTGRES_PASSWORD` and `AUTH_SECRET` in `.env`
- **Architecture:** Builder stages compile on the build platform; runtime stage runs anywhere with `node:20-alpine`

## Frequently Asked Questions

### What ports does Kaneo expose?

Kaneo exposes port `5173` for both the web UI and API. The same port serves static frontend assets and proxies API requests—no separate ports required.

### Can I use an external PostgreSQL database?

Yes. Set `DATABASE_URL` in your `.env` file with your external connection string, and remove or disable the `postgres` service in your compose file.

### How do I update to a new Kaneo version?

For pre-built images: `docker compose -f compose.yml pull && docker compose -f compose.yml up -d`. For local builds: `git pull` then `docker compose -f compose.local.yml up -d --build`.

### Why does the runtime stage use nginx?

Nginx serves the compiled web frontend's static assets with optimal compression and caching headers. The API runs as a separate Node.js process, with nginx proxying requests to `/api/*` paths.