Skip to content
Keenpix docs
Self-hosting

Configuration

Environment variables for a self-hosted instance.

Set these in .env (or your orchestrator's secrets).

Core

VariableRequiredDefaultPurpose
DATABASE_URLyesPostgres connection string
POSTGRES_PASSWORDyes (compose)Password for the bundled Compose Postgres service
BETTER_AUTH_SECRETyes (prod)Session secret; must be 32+ random chars
BETTER_AUTH_URLnohttp://localhost:3000Public base URL for auth callbacks and secure-cookie behavior
KEENPIX_APP_URLnoBETTER_AUTH_URLCanonical app URL for generated metadata, docs links, and OG URLs
KEENPIX_SUPER_ADMIN_EMAILyesEmail for the bootstrapped super admin account
KEENPIX_SUPER_ADMIN_PASSWORDyesPassword for the bootstrapped super admin account
KEENPIX_ADMIN_EMAILlegacy aliasBackward-compatible alias for KEENPIX_SUPER_ADMIN_EMAIL
KEENPIX_ADMIN_PASSWORDlegacy aliasBackward-compatible alias for KEENPIX_SUPER_ADMIN_PASSWORD
KEENPIX_RUN_MIGRATIONSnotrue in DockerDocker entrypoint switch for running migrations before start
KEENPIX_RUN_SEEDnotrue in DockerDocker entrypoint switch for seeding the default org and super admin before start
KEENPIX_CACHE_DIRno./.keenpix-cacheWhere transformed images are cached on disk
KEENPIX_MODEnoselfhostselfhost (default) or cloud. Self-host disables the marketing site, self-signup, and billing
LOG_LEVELnoinfoServer log level: fatal, error, warn, info, debug, trace, or silent
KEENPIX_LOG_DIRnoAlso retain rotating evlog NDJSON files in this directory; stdout logging remains enabled

Browser public URLs

These Vite variables are only needed when the browser bundle must call a non-default public app or auth URL.

VariableDefaultPurpose
VITE_KEENPIX_PUBLIC_URLBrowser-facing app URL
VITE_KEENPIX_AUTH_URLBackward-compatible auth URL alias
VITE_BETTER_AUTH_URLBackward-compatible Better Auth URL alias

Transactional email

Verification, password reset, and teammate invitation emails go through exactly one provider, selected with EMAIL_PROVIDER. Email is configured entirely from the environment — there is no in-app email settings screen. Leave EMAIL_PROVIDER unset to disable email (fine for a single-admin install that never invites teammates). When it is set, the selected provider's required variables must be present or the app fails to start.

VariableDefaultPurpose
EMAIL_PROVIDERpostmark, resend, or smtp (unset = off)

EMAIL_PROVIDER=postmark

VariableDefaultPurpose
POSTMARK_API_KEYPostmark server token (required)
POSTMARK_FROMVerified sender, e.g. Keenpix <no-reply@…> (required)
POSTMARK_MESSAGE_STREAMoutboundMessage stream to send on

EMAIL_PROVIDER=resend

VariableDefaultPurpose
RESEND_API_KEYResend API key (required)
RESEND_FROMVerified sender, e.g. Keenpix <no-reply@…> (required)

EMAIL_PROVIDER=smtp

Point at any SMTP relay (a provider, or a self-hosted Postfix/Mailpit).

VariableDefaultPurpose
SMTP_HOSTSMTP server hostname (required)
SMTP_FROM_EMAILSender email address (required)
SMTP_PORT587SMTP server port
SMTP_SECUREfalsetrue for implicit TLS, false for STARTTLS
SMTP_USERSMTP username
SMTP_PASSWORDSMTP password
SMTP_FROM_NAMESender display name

Cloudflare edge analytics

Optional. Wire a Cloudflare API token so keenpix can show real edge cache hit-rate alongside its origin-shield figures (see edge analytics). keenpix now persists hourly edge rollups, so edge analytics covers any range and accumulates history beyond Cloudflare's ~24h adaptive window — coverage grows forward as the dashboard is used. Configure it with the environment variables below; the effective status is shown read-only at Admin → Settings. The token only needs the zone-scoped Analytics → Read permission.

VariableDefaultPurpose
CLOUDFLARE_API_TOKENZone-scoped API token with Analytics → Read
CLOUDFLARE_ZONE_ID32-character hex zone id for the image hostname
CLOUDFLARE_HOSTOptional. A Cloudflare zone can serve several subdomains and edge analytics only filter by path (/img/*), so set this to the keenpix hostname (e.g. images.example.com) when the zone also hosts other sites that use a /img path. Leave blank to count the whole zone.

Limits & performance

VariableDefaultPurpose
KEENPIX_CACHE_MAX_BYTEStransform default: 2147483648 (2GB); Docker/Coolify: 8589934592 (8GB)Disk cache LRU cap
KEENPIX_CACHE_STALE_MS86400000 (24h)Serve stale disk entries immediately and refresh them in the background; 0 disables stale refresh
KEENPIX_MEMORY_CACHE_MAX_BYTEStransform default: 67108864 (64MB); Docker/Coolify: 268435456 (256MB)In-process hot variant LRU cap; 0 disables memory caching
KEENPIX_CACHE_REDIS_URLCompose: redis://dragonfly:6379Shared Dragonfly variant tier; separate setting from the BullMQ queue URL
KEENPIX_CACHE_DRAGONFLY_MAX_BYTES536870912 (512MB)Dragonfly variant LRU budget
KEENPIX_CACHE_DELETE_AFTER_MS2592000000 (30d)Terminal age after which a variant is deleted from every tier; 0 disables
KEENPIX_CACHE_S3_BUCKETShared S3/R2/MaxIO cache bucket
KEENPIX_CACHE_S3_ENDPOINTS3-compatible endpoint
KEENPIX_CACHE_S3_REGIONautoS3-compatible region
KEENPIX_CACHE_S3_ACCESS_KEY_IDS3-compatible access key
KEENPIX_CACHE_S3_SECRET_ACCESS_KEYS3-compatible secret key
KEENPIX_MAX_ORIGIN_BYTES52428800 (50MB)Max bytes pulled from an origin before 413
KEENPIX_MAX_WATERMARK_BYTES5242880 (5MB)Max bytes pulled for a project watermark asset
KEENPIX_QUEUE_URLredis://127.0.0.1:6379BullMQ connection URL; Compose uses Dragonfly
KEENPIX_WORKBENCH_USERNAMEBasic-auth username for the internal worker dashboard; set with the password
KEENPIX_WORKBENCH_PASSWORDBasic-auth password for the internal worker dashboard; set with the username
KEENPIX_WORKER_SECRET32+ character secret for worker-to-app callbacks
KEENPIX_WORKER_CONCURRENCY4Concurrent prewarm jobs per worker process
KEENPIX_WORKER_PORT3001Internal health and Workbench listener for the worker container; Compose does not publish it
KEENPIX_WORKER_TIMEOUT_MS60000Maximum transform wait for one prewarm job
KEENPIX_TRANSFORM_URLInternal transform service URL used by app and worker
KEENPIX_TRANSFORM_PORT3002Transform HTTP listener
KEENPIX_MAX_INPUT_PIXELS50000000Decode-time pixel ceiling (decompression-bomb guard)
KEENPIX_MAX_DIMENSION4096Longest output side when a request gives no w/h
KEENPIX_ORIGIN_TIMEOUT_MS10000Per-attempt origin fetch timeout before 504

Compose images and ports

VariableDefaultPurpose
KEENPIX_APP_IMAGEghcr.io/lord007tn/keenpix-app:latestControl-plane image
KEENPIX_TRANSFORM_IMAGEghcr.io/lord007tn/keenpix-transform:latestTransform data-plane image
KEENPIX_WORKER_IMAGEghcr.io/lord007tn/keenpix-worker:latestWorker and Workbench image
KEENPIX_DOCS_IMAGEghcr.io/lord007tn/keenpix-docs:latestStandalone docs image
KEENPIX_PORT3000Published app port in self-host Compose
KEENPIX_DOCS_PORT3003Published docs port in self-host Compose

Pin all four images to the same release tag in production.

On this page