🎯 All you need. Nothing you don't. Open source project management that works for you, not against you.
docker pull usekaneo/kaneo:latest
docker run -d --name kaneo --restart always usekaneo/kaneo:latest
docker compose up -d
---
title: The compose.yml file
description: The Docker Compose file is used to start the required services.
---
## The initial compose file
The first step is to create a Docker Compose file. We will call it `compose.yml`.
```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
Copy `.env.sample` to `.env` and uncomment or set these values:
```env
KANEO_CLIENT_URL=http://localhost:5173
POSTGRES_DB=kaneo
POSTGRES_USER=kaneo
POSTGRES_PASSWORD=changeme
AUTH_SECRET= # generate: openssl rand -hex 32
```
`KANEO_API_URL` defaults to `KANEO_CLIENT_URL/api`; set it 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](/core/installation/storage-backends).
### Optional MinIO service
Add this service and volume to your `compose.yml` only if you want uploads:
```yml
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](/core/installation/environment-variables).
### Example `.env` values for MinIO
```env
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`](https://github.com/ferdzo/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.