What you’ll set up
Kan
Kan web app (Next.js), on port 3000.
PostgreSQL 15
PostgreSQL database for Kan.
MinIO (S3-compatible)
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.
NEXT_PUBLIC_BASE_URL
→ the Kan siteNEXT_PUBLIC_STORAGE_URL
→ the public S3 base URLNEXT_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.1
Set environment variables
Provide the minimum required configuration (local example):Optional (see README for full list): Email (
Issue #109 fix: make sure
NEXT_PUBLIC_STORAGE_DOMAIN
exactly
equals the hostname that serves your images (no scheme, no port).EMAIL_FROM
, SMTP_HOST
, SMTP_PORT
, SMTP_USER
, SMTP_PASSWORD
, SMTP_SECURE
), OAuth/OIDC (GOOGLE_*
, GITHUB_*
, OIDC_*
), auth toggles, Trello import, etc.2
Create or review Docker Compose files
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.Docker Compose example
Docker Compose example
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
andS3_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
3
Start services
- Kan: http://kan.localtest.me:3000
- MinIO Console: http://minio.localtest.me:9001
- MinIO S3 API: http://s3.localtest.me:9000
4
Initialize MinIO
- Log into the MinIO Console (http://minio.localtest.me:9001).
-
Create a bucket (e.g.,
kan
). - For simple public avatars, apply a read-only policy so GET requests are allowed for objects:
Alternatively, keep the bucket private and use presigned URLs. In that case,
ensure your server and browser access paths are correctly configured.
5
Verify the setup
- Sign in to Kan and upload an avatar (Settings).
- Confirm the object is created in your MinIO bucket.
- The avatar should render without errors.
/_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.
Troubleshooting
Files upload but don’t display
Files upload but don’t display
- 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.
Make the bucket public (read-only) with mc
Make the bucket public (read-only) with mc
Use your MinIO root credentials to allow anonymous reads:
Alternative: S3 bucket policy (AWS CLI)
Alternative: S3 bucket policy (AWS CLI)
If you prefer a bucket policy, apply a public-read policy for objects:
policy.json
Next.js optimizer 400 — url parameter is not allowed
Next.js optimizer 400 — url parameter is not allowed
- Exact match on
NEXT_PUBLIC_STORAGE_DOMAIN
with your storage host. - Same host in
NEXT_PUBLIC_STORAGE_URL
. - Update to the latest Kan image.
Next/Image: “url parameter is valid but upstream response is invalid”
Next/Image: “url parameter is valid but upstream response is invalid”
- 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):
Connectivity checks
Connectivity checks
- The Kan container must reach
S3_ENDPOINT
. - Verify DNS/ports inside the container (e.g.,
docker exec -it <kan-container> sh
).