Skip to content
Keenpix docs
Self-hosting

Deploy

Deploy Keenpix with Docker and verify the stateful parts.

Keenpix deploys as four focused Node applications plus Postgres and Dragonfly. The default Compose file exposes the control plane on port 3000 and the standalone docs site on 3003; the transform and worker services remain private on the Compose network.

Weighing self-host against the hosted option? See self-hosted vs managed image optimization.

Deploy a pinned release

git clone --branch v0.3.0 --depth 1 https://github.com/lord007tn/keenpix.git
cd keenpix
cp .env.example .env
# keep every service on the release you just checked out:
echo 'KEENPIX_APP_IMAGE=ghcr.io/lord007tn/keenpix-app:v0.3.0' >> .env
echo 'KEENPIX_TRANSFORM_IMAGE=ghcr.io/lord007tn/keenpix-transform:v0.3.0' >> .env
echo 'KEENPIX_WORKER_IMAGE=ghcr.io/lord007tn/keenpix-worker:v0.3.0' >> .env
echo 'KEENPIX_DOCS_IMAGE=ghcr.io/lord007tn/keenpix-docs:v0.3.0' >> .env
# set a strong secret:
#   BETTER_AUTH_SECRET=$(openssl rand -hex 32)
# set POSTGRES_PASSWORD, KEENPIX_SUPER_ADMIN_EMAIL, and KEENPIX_SUPER_ADMIN_PASSWORD
docker compose pull
docker compose up -d
# dashboard → http://localhost:3000
# docs      → http://localhost:3003

docker-compose.yml ships the app, transform data plane, BullMQ worker with Workbench, docs, Postgres, and Dragonfly. On boot the app runs prisma migrate deploy, optionally seeds the default organization and super admin, then starts as a non-root user.

Check the release page before changing the four image variables; keeping explicit tags prevents a later pull from replacing only part of the running release.

After deploy

  1. Sign in as KEENPIX_SUPER_ADMIN_EMAIL with KEENPIX_SUPER_ADMIN_PASSWORD.
  2. Create a project pointed at your image origin.
  3. Add your source host under Settings → Security → Allowed hosts.
  4. Invite operators from the Admin console → Staff (/admin/staff) and copy the invitation link. Transactional email is configured entirely through the environment (EMAIL_PROVIDER + its provider vars) — there is no in-app email screen, and those env values are the only source. With email configured, the invite is also sent automatically.
  5. Request /img/https://your-origin.example/photo.jpg?project=ID&w=1200&fmt=webp — no API key required for transform URLs.

Architecture

Browser/CDN ──▶ app:3000 ──▶ transform:3002 ──▶ customer origin
                    │              │
                    │              └── memory + disk cache volume
                    ├── Postgres
                    └── Dragonfly ──▶ worker:3001

Operator ──▶ docs:3003

Scale the transform and worker independently. For multiple transform replicas, configure a shared S3-compatible cache such as R2 or MaxIO. See Docker Compose, Coolify, CDN setup for Cloudflare, Nginx, and Caddy caching rules, Deployment presets for ready-made .env files, and Configuration for every environment variable.

On this page