Skip to main content

The initial compose file

The first step is to create a Docker Compose file. We will call it compose.yml.
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
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5173/api/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 60s

volumes:
  postgres_data:

The services and volumes

Kaneo uses the following services:
  • postgres: The PostgreSQL database.
  • kaneo: The combined API and web container.
Additionally, the following volumes are used:
  • postgres_data: The PostgreSQL data.

Required URL configuration

For the combined image, only KANEO_CLIENT_URL is required — KANEO_API_URL is derived automatically as KANEO_CLIENT_URL/api:
KANEO_CLIENT_URL=http://localhost:5173
POSTGRES_PASSWORD=changeme
AUTH_SECRET=                             # generate: openssl rand -hex 32
Set KANEO_API_URL explicitly only if you need to override the default (e.g. a reverse proxy changes the path). This keeps the browser on one origin while the container proxies /api requests to the internal API process. For this Compose setup, the bundled Kaneo container reaches PostgreSQL at the service hostname postgres. If you run the API directly on your host instead, use localhost or set DATABASE_URL explicitly.

Notes on object storage

Kaneo uses S3-compatible object storage for private uploads in task descriptions and comments.
  • Object storage is optional. Kaneo runs without it, but uploads in task descriptions and comments will be unavailable.
  • For local/self-hosted setups, MinIO is the recommended option when you want uploads.
  • The Kaneo API creates presigned upload URLs and the browser uploads files directly to the configured storage backend.
  • Kaneo serves uploaded assets back through its own API, so the bucket does not need to be public.
  • If you do not configure object storage, leave the S3_* variables unset.
  • For a complete MinIO example and backend-specific setup notes, see the storage backends guide.

Optional MinIO service

Add this service and volume to your compose.yml only if you want uploads:
services:
  minio:
    image: minio/minio:latest
    command: server /data --console-address ":9001"
    env_file:
      - .env
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - minio_data:/data
    restart: unless-stopped

volumes:
  minio_data:
Set the matching S3_* environment variables in your .env file. See the environment variables page.

Example .env values for MinIO

MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin

S3_ENDPOINT=http://minio:9000
S3_BUCKET=kaneo-uploads
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_REGION=us-east-1
S3_FORCE_PATH_STYLE=true
Create the bucket before testing uploads. With MinIO, you can do that from the MinIO Console at http://localhost:9001.

Using fs instead of MinIO

If you want to use fs, you can point the same S3_* variables at its endpoint instead of MinIO. Kaneo only needs an S3-compatible API endpoint, credentials, and a bucket. When using fs, keep these differences in mind:
  • Set S3_FORCE_PATH_STYLE=true.
  • Create buckets through the S3 API or CLI, not through a web console.
  • Browser uploads may require reverse-proxy CORS handling, because fs does not currently implement S3 CORS APIs.
After creating the compose file, the next step is to set up the environment variables.