From 2a0220ce6fbb8783090180a3f2f1946c9ee9cfb0 Mon Sep 17 00:00:00 2001 From: Morfixx <63129378+nahSystemu@users.noreply.github.com> Date: Fri, 19 Sep 2025 23:08:59 +0300 Subject: [PATCH] feat: added docs to website (#186) * feat(docs): self-hosting s3 minio guide * feat(docs): self-hosting introduction guide * fix: build error * refactor: use kan over kan.bn --------- Co-authored-by: Henry --- .../docs/guides/self-hosting/introduction.mdx | 140 ++++++ apps/docs/guides/self-hosting/s3.mdx | 397 ++++++++++++++++++ apps/docs/mint.json | 12 + 3 files changed, 549 insertions(+) create mode 100644 apps/docs/guides/self-hosting/introduction.mdx create mode 100644 apps/docs/guides/self-hosting/s3.mdx diff --git a/apps/docs/guides/self-hosting/introduction.mdx b/apps/docs/guides/self-hosting/introduction.mdx new file mode 100644 index 00000000..5b17d36a --- /dev/null +++ b/apps/docs/guides/self-hosting/introduction.mdx @@ -0,0 +1,140 @@ +--- +title: "Introduction" +description: "Overview and quick start to run Kan on your own infrastructure using Docker Compose." +mode: "wide" +tag: "NEW" +--- + +This guide introduces how to self-host Kan. It starts with the minimal Docker Compose setup (web + PostgreSQL) and points you to optional features like email and S3-based file storage. + +## What you’ll set up + + + + Next.js application served on port 3000. + + + Primary database for Kan data. + + + + + For file uploads (avatars), OAuth, and other advanced options, see the + Environment Variables section in the README and the dedicated [S3 + guide](/guides/self-hosting/s3). The [full + compose](https://github.com/kanbn/kan/blob/main/docker-compose.yml) in the + repo includes a richer configuration via .env. + + +## Prerequisites + +- Docker and Docker Compose +- A long random string for BETTER_AUTH_SECRET (32+ chars) + +## Quick start + + + + Paste the following minimal configuration into a new docker-compose.yml file: + + ```yaml + services: + web: + image: ghcr.io/kanbn/kan:latest + container_name: kan-web + ports: + - "3000:3000" + networks: + - kan-network + environment: + NEXT_PUBLIC_BASE_URL: http://localhost:3000 + BETTER_AUTH_SECRET: your_auth_secret + POSTGRES_URL: postgresql://kan:your_postgres_password@postgres:5432/kan_db + NEXT_PUBLIC_ALLOW_CREDENTIALS: true + depends_on: + - postgres + restart: unless-stopped + + postgres: + image: postgres:15 + container_name: kan-db + environment: + POSTGRES_DB: kan_db + POSTGRES_USER: kan + POSTGRES_PASSWORD: your_postgres_password + ports: + - 5432:5432 + volumes: + - kan_postgres_data:/var/lib/postgresql/data + restart: unless-stopped + networks: + - kan-network + + networks: + kan-network: + + volumes: + kan_postgres_data: + ``` + + + The example above is intentionally minimal. The repository provides a more feature-complete compose file at [docker-compose.yml](https://github.com/kanbn/kan/blob/main/docker-compose.yml) if you want environment-based configuration, OAuth, S3, and more. + + + + + + Bring everything up in detached mode: + + ```bash + docker compose up -d + ``` + + Once started, open [http://localhost:3000](http://localhost:3000). + + + + + Useful commands while developing or testing: + + - Stop the containers: docker compose down + - View logs: docker compose logs -f + - Restart: docker compose restart + + + + + For a production-like setup and more features (email, OAuth, file uploads, etc.), create a .env file and set the relevant variables shown in the README’s Environment Variables section. + + + ```bash + # Required + NEXT_PUBLIC_BASE_URL=http://localhost:3000 + BETTER_AUTH_SECRET=replace_with_long_random_string + POSTGRES_URL=postgresql://kan:your_postgres_password@postgres:5432/kan_db + + # Optional: Email + EMAIL_FROM="Kan " + SMTP_HOST=smtp.resend.com + SMTP_PORT=465 + SMTP_USER=resend + SMTP_PASSWORD=re_xxxx + SMTP_SECURE=true + + # Optional: Auth toggles + NEXT_PUBLIC_ALLOW_CREDENTIALS=true + NEXT_PUBLIC_DISABLE_SIGN_UP=false + ``` + + + If you plan to enable file uploads (avatars, etc.), you’ll also need S3 variables (S3_ENDPOINT, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, NEXT_PUBLIC_STORAGE_URL, NEXT_PUBLIC_STORAGE_DOMAIN, …). See the S3 guide linked at the top. + + + + + + +## Reference + +- [GitHub README](https://github.com/kanbn/kan/blob/main/README.md#self-hosting-) +- [GitHub docker-compose.yml](https://github.com/kanbn/kan/blob/main/docker-compose.yml) diff --git a/apps/docs/guides/self-hosting/s3.mdx b/apps/docs/guides/self-hosting/s3.mdx new file mode 100644 index 00000000..29d8f2f5 --- /dev/null +++ b/apps/docs/guides/self-hosting/s3.mdx @@ -0,0 +1,397 @@ +--- +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. + + + + + +
    +
  • Sign in to Kan and upload an avatar (Settings).
  • +
  • Confirm the object is created in your MinIO bucket.
  • +
  • The avatar should render without errors.
  • +
+ + If you see a 400 from /\_next/image with “url parameter is not allowed”: + +
    +
  • + NEXT_PUBLIC_STORAGE_DOMAIN must exactly match the S3 hostname + that serves images. +
  • +
  • + NEXT_PUBLIC_STORAGE_URL should use the same host (with + scheme/port). +
  • +
  • Ensure you’re using the latest Kan image.
  • +
+ +
+
+ +## Production setup + + + + +
    +
  • NEXT_PUBLIC_BASE_URL=http://kan.localtest.me:3000
  • +
  • S3_ENDPOINT=http://s3.localtest.me:9000
  • +
  • NEXT_PUBLIC_STORAGE_URL=http://s3.localtest.me:9000
  • +
  • NEXT_PUBLIC_STORAGE_DOMAIN=s3.localtest.me
  • +
  • Keep S3_FORCE_PATH_STYLE=true for MinIO.
  • +
+
+ + +
    +
  • NEXT_PUBLIC_BASE_URL=https://kan.example.com
  • +
  • S3_ENDPOINT=https://s3.example.com
  • +
  • NEXT_PUBLIC_STORAGE_URL=https://s3.example.com
  • +
  • NEXT_PUBLIC_STORAGE_DOMAIN=s3.example.com
  • +
  • Keep S3_FORCE_PATH_STYLE=true for MinIO.
  • +
  • Put Kan and MinIO behind HTTPS with a reverse proxy (Traefik/Nginx) and valid TLS.
  • +
+
+
+ +## Troubleshooting + + + +
    +
  • If public: confirm GET is allowed on objects (bucket policy).
  • +
  • If private: ensure presigned URLs are generated and valid.
  • +
  • 403 AccessDenied indicates permissions, not CORS. CORS is not required for simple <img> GETs.
  • +
+
+ + + 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 + ``` + + + + +
    +
  • + Exact match on NEXT_PUBLIC_STORAGE_DOMAIN with your storage + host. +
  • +
  • + Same host in NEXT_PUBLIC_STORAGE_URL. +
  • +
  • Update to the latest Kan image.
  • +
+
+ + +
    +
  • This means Next.js accepted the URL, but the upstream returned a non-image (e.g., 403 HTML/XML).
  • +
  • Fix: make the bucket/object publicly readable (see above), or use presigned URLs.
  • +
  • Sanity test from the web network (replace with your image URL):
  • +
+ + ```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" + ``` + +
+ + +
    +
  • The Kan container must reach S3_ENDPOINT.
  • +
  • Verify DNS/ports inside the container (e.g., docker exec -it <kan-container> sh).
  • +
+
+
+ +## 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) diff --git a/apps/docs/mint.json b/apps/docs/mint.json index 078057bd..0040c99a 100644 --- a/apps/docs/mint.json +++ b/apps/docs/mint.json @@ -44,6 +44,18 @@ "group": "Get Started", "pages": ["introduction"] }, + { + "group": "Guides", + "pages": [ + { + "group": "Self-Hosting", + "pages": [ + "guides/self-hosting/introduction", + "guides/self-hosting/s3" + ] + } + ] + }, { "group": "Import", "pages": ["imports/trello"]