--- title: "Kan + MinIO (S3)" mode: "wide" tag: "NEW" --- Deploy Kan with PostgreSQL and MinIO (S3-compatible storage) using Docker Compose, with clear steps and production notes. ## What you’ll set up Kan web app (Next.js), on port 3000. PostgreSQL database for Kan. MinIO object storage (console port 9001, S3 API port 9000). ## How it works - Kan stores data in PostgreSQL. - Kan uploads files (e.g., avatars) to MinIO over the S3 API. - The browser fetches public files directly from MinIO’s public URL. - The Next.js image optimizer in Kan must be explicitly allowed to fetch from your storage host. Key domain settings: - NEXT_PUBLIC_BASE_URL → the Kan site - NEXT_PUBLIC_STORAGE_URL → the public S3 base URL - NEXT_PUBLIC_STORAGE_DOMAIN → the exact S3 hostname (no scheme) ## Prerequisites - Docker and Docker Compose - Open local ports: 3000 (Kan), 5432 (Postgres), 9000/9001 (MinIO) - A long random string for BETTER_AUTH_SECRET (32+ chars) For production you’ll want a reverse proxy (Traefik/Nginx/Caddy), valid TLS certificates, and DNS for your domains (e.g., kan.example.com,{" "} s3.example.com). ## Quick start Why localtest.me? It resolves to 127.0.0.1{" "} automatically, so you can test domain-based configs locally without editing hosts. Provide the minimum required configuration (local example): ```bash NEXT_PUBLIC_BASE_URL=http://kan.localtest.me:3000 BETTER_AUTH_SECRET= POSTGRES_URL=postgresql://kan:@postgres:5432/kan_db # MinIO/S3 S3_ENDPOINT=http://s3.localtest.me:9000 S3_ACCESS_KEY_ID= S3_SECRET_ACCESS_KEY= S3_REGION=none S3_FORCE_PATH_STYLE=true # Public storage access NEXT_PUBLIC_STORAGE_URL=http://s3.localtest.me:9000 NEXT_PUBLIC_STORAGE_DOMAIN=s3.localtest.me NEXT_PUBLIC_AVATAR_BUCKET_NAME=kan ``` Issue #109 fix: make sure NEXT_PUBLIC_STORAGE_DOMAIN exactly equals the hostname that serves your images (no scheme, no port). Optional (see README for full list): Email (`EMAIL_FROM`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_SECURE`), OAuth/OIDC (`GOOGLE_*`, `GITHUB_*`, `OIDC_*`), auth toggles, Trello import, etc. You can start from the minimal compose at the repository root (docker-compose.yml) and review the production-oriented settings in cloud/docker-compose.yml. Start with the minimal setup (web + postgres + minio) and ensure environment variables are passed to the web service. ```yaml services: postgres: image: postgres:15 container_name: kan-db ports: - "5432:5432" environment: POSTGRES_USER: kan POSTGRES_PASSWORD: changeme POSTGRES_DB: kan_db volumes: - pg_data:/var/lib/postgresql/data restart: unless-stopped minio: image: minio/minio:latest container_name: kan-minio command: server /data --console-address ":9001" ports: - "9000:9000" # S3 API - "9001:9001" # Console environment: # Use the same credentials in your .env # as S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY MINIO_ROOT_USER: minio MINIO_ROOT_PASSWORD: minio123456789 volumes: - minio_data:/data restart: unless-stopped web: image: ghcr.io/kanbn/kan:latest container_name: kan-web depends_on: - postgres - minio ports: - "3000:3000" # Load variables from .env # (see the "Set environment variables" step) env_file: - .env restart: unless-stopped volumes: pg_data: minio_data: ``` Ensure your .env contains values that match this compose file. For example:
  • POSTGRES_URL=postgresql://kan:changeme@postgres:5432/kan_db
  • S3_ENDPOINT=http://s3.localtest.me:9000
  • S3_ACCESS_KEY_ID=minio and{" "} S3_SECRET_ACCESS_KEY=minio123456789
  • S3_FORCE_PATH_STYLE=true
  • NEXT_PUBLIC_STORAGE_URL=http://s3.localtest.me:9000
  • NEXT_PUBLIC_STORAGE_DOMAIN=s3.localtest.me
  • NEXT_PUBLIC_AVATAR_BUCKET_NAME=kan
```bash docker compose up -d ``` Then open: 1) Log into the MinIO Console (http://minio.localtest.me:9001). 2. Create a bucket (e.g., kan). 3. For simple public avatars, apply a read-only policy so GET requests are allowed for objects: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": ["*"] }, "Action": ["s3:GetBucketLocation", "s3:ListBucket"], "Resource": ["arn:aws:s3:::kan"] }, { "Effect": "Allow", "Principal": { "AWS": ["*"] }, "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::kan/*"] } ] } ``` Alternatively, keep the bucket private and use presigned URLs. In that case, ensure your server and browser access paths are correctly configured. If you see a 400 from /\_next/image with “url parameter is not allowed”:
## Production setup ## Troubleshooting Use your MinIO root credentials to allow anonymous reads: ```bash # Replace with your MINIO_ROOT_PASSWORD MINIO_PASS='' # Point mc at MinIO via the container network (no ports required) docker run --rm --network container:kan-minio minio/mc \ mc alias set local http://127.0.0.1:9000 minio "$MINIO_PASS" # Allow public downloads from the bucket docker run --rm --network container:kan-minio minio/mc \ mc anonymous set download local/kan # Optional: verify anonymous status docker run --rm --network container:kan-minio minio/mc \ mc anonymous get local/kan ``` If you prefer a bucket policy, apply a public-read policy for objects: ```json policy.json { "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::kan/*"] } ] } ``` ```bash # Use your MinIO root credentials MINIO_PASS='' docker run --rm --network container:kan-minio \ -e AWS_ACCESS_KEY_ID=minio \ -e AWS_SECRET_ACCESS_KEY="$MINIO_PASS" \ -e AWS_DEFAULT_REGION=us-east-1 -e AWS_S3_FORCE_PATH_STYLE=true \ -v "$PWD:/work" amazon/aws-cli \ s3api put-bucket-policy --bucket kan --policy file:///work/policy.json \ --endpoint-url http://127.0.0.1:9000 ``` ```bash IMG_URL="https://s3.example.com/kan/path/to/avatar.jpg" # Headers/content-type as seen from the app network docker run --rm --network container:kan-web curlimages/curl:8.9.1 \ -I -L --max-redirs 5 "$IMG_URL" # Quick status + content-type summary docker run --rm --network container:kan-web curlimages/curl:8.9.1 \ -s -o /dev/null -w "HTTP:%{http_code} CT:%{content_type} URL:%{url_effective}\n" -L "$IMG_URL" ``` ## References - [Kan README](https://github.com/kanbn/kan/blob/main/README.md) - [Cloud compose reference](https://github.com/kanbn/kan/blob/main/cloud/docker-compose.yml) - [Kan #109 Issue](https://github.com/kanbn/kan/issues/109)