Docker / Self-Hosted
The Docker adapter lets you run CruzJS anywhere — Dokploy, Coolify, bare Docker, Docker Compose, or Kubernetes.
Installation
Section titled “Installation”npm install @cruzjs/adapter-dockerimport { DockerAdapter } from '@cruzjs/adapter-docker';
export default createCruzApp({ schema, modules: [/* your modules */], adapter: new DockerAdapter({ databaseUrl: process.env.DATABASE_URL, redisUrl: process.env.REDIS_URL, s3Bucket: process.env.S3_BUCKET, s3Endpoint: process.env.S3_ENDPOINT, // For MinIO openaiApiKey: process.env.OPENAI_API_KEY, openaiBaseUrl: process.env.OPENAI_BASE_URL, // For Ollama }), pages: () => import('virtual:react-router/server-build'),});Service Mapping
Section titled “Service Mapping”| CruzJS Binding | Self-Hosted Service |
|---|---|
| Database | PostgreSQL, MySQL, SQLite |
| Cache | Redis, In-memory |
| Storage | MinIO (S3-compatible), Local filesystem |
| Queue | BullMQ (Redis-backed) |
| AI | Ollama, vLLM, OpenAI API |
Dockerfile
Section titled “Dockerfile”A multi-stage Dockerfile for production builds:
# Stage 1: Install dependenciesFROM node:20-alpine AS depsWORKDIR /appCOPY package.json package-lock.json ./RUN npm ci --omit=dev
# Stage 2: Build the applicationFROM node:20-alpine AS builderWORKDIR /appCOPY package.json package-lock.json ./RUN npm ciCOPY . .RUN npx cruz build
# Stage 3: Production imageFROM node:20-alpine AS runnerWORKDIR /app
# Create non-root userRUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 cruzjs
# Copy build output and production dependenciesCOPY --from=deps /app/node_modules ./node_modulesCOPY --from=builder /app/build ./buildCOPY --from=builder /app/package.json ./COPY --from=builder /app/src/database/migrations ./src/database/migrations
# Set ownershipRUN chown -R cruzjs:nodejs /appUSER cruzjs
EXPOSE 3000
# Health checkHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
CMD ["node", "build/server/index.js"].dockerignore
Section titled “.dockerignore”node_modules.git.env.env.*data/*.md.cruz-agent/Docker Compose
Section titled “Docker Compose”A complete development and production setup with all services:
services: app: build: . ports: - "3000:3000" environment: NODE_ENV: production DATABASE_URL: postgres://cruz:cruz@postgres:5432/cruzdb REDIS_URL: redis://redis:6379 S3_ENDPOINT: http://minio:9000 S3_BUCKET: uploads S3_ACCESS_KEY: minioadmin S3_SECRET_KEY: minioadmin AUTH_SECRET: change-me-in-production depends_on: postgres: condition: service_healthy redis: condition: service_healthy minio: condition: service_started restart: unless-stopped
postgres: image: postgres:16-alpine environment: POSTGRES_DB: cruzdb POSTGRES_USER: cruz POSTGRES_PASSWORD: cruz volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U cruz -d cruzdb"] interval: 5s timeout: 5s retries: 5 restart: unless-stopped
redis: image: redis:7-alpine command: redis-server --appendonly yes volumes: - redis_data:/data ports: - "6379:6379" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 restart: unless-stopped
minio: image: minio/minio command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin volumes: - minio_data:/data ports: - "9000:9000" - "9001:9001" restart: unless-stopped
# Optional: Create the default bucket on startup minio-init: image: minio/mc depends_on: - minio entrypoint: > /bin/sh -c " sleep 3; mc alias set myminio http://minio:9000 minioadmin minioadmin; mc mb myminio/uploads --ignore-existing; exit 0; "
volumes: postgres_data: redis_data: minio_data:Running with Docker Compose
Section titled “Running with Docker Compose”# Build and start all servicesdocker compose up -d
# Run database migrationsdocker compose exec app npx cruz db migrate
# Seed the database (optional)docker compose exec app npx cruz db seed
# View logsdocker compose logs -f app
# Stop all servicesdocker compose downEnvironment Variables Reference
Section titled “Environment Variables Reference”| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | PostgreSQL, MySQL, or SQLite connection string |
REDIS_URL | No | Redis connection string for cache and queues |
S3_ENDPOINT | No | S3-compatible endpoint (MinIO, Backblaze B2) |
S3_BUCKET | No | Bucket name for file storage |
S3_ACCESS_KEY | No | S3 access key |
S3_SECRET_KEY | No | S3 secret key |
S3_REGION | No | S3 region (default: us-east-1) |
OPENAI_API_KEY | No | OpenAI API key |
OPENAI_BASE_URL | No | Custom OpenAI-compatible endpoint (Ollama, vLLM) |
AUTH_SECRET | Yes | Session encryption key |
PORT | No | Server port (default: 3000) |
Database Migrations in Docker
Section titled “Database Migrations in Docker”Running Migrations on Deploy
Section titled “Running Migrations on Deploy”Add a migration step to your deployment pipeline:
# Option 1: Run migrations as a separate command after deploydocker compose exec app npx cruz db migrate
# Option 2: Run migrations in a one-off containerdocker compose run --rm app npx cruz db migrateAutomatic Migrations on Startup
Section titled “Automatic Migrations on Startup”Add a startup script that runs migrations before starting the server:
# In DockerfileCOPY start.sh ./RUN chmod +x start.shCMD ["./start.sh"]#!/bin/shecho "Running database migrations..."npx cruz db migrateecho "Starting server..."node build/server/index.jsHealth Checks
Section titled “Health Checks”The Docker adapter responds to health check requests at /api/health:
// Built-in health check response// GET /api/health → { "status": "ok", "timestamp": "..." }Configure health checks in Docker Compose:
healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"] interval: 30s timeout: 5s start_period: 10s retries: 3For Kubernetes:
livenessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 10 periodSeconds: 30readinessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 5 periodSeconds: 10Deployment to Dokploy
Section titled “Deployment to Dokploy”Dokploy is a self-hosted deployment platform (alternative to Vercel/Netlify).
- Push your CruzJS app to a Git repository
- In Dokploy, create a new application and connect your repository
- Set the build method to “Dockerfile”
- Add environment variables in the Dokploy dashboard
- Deploy
Dokploy automatically builds your Docker image, runs it, and provides SSL via Let’s Encrypt.
Deployment to Coolify
Section titled “Deployment to Coolify”Coolify is another self-hosted deployment platform.
- Connect your Git repository in Coolify
- Select “Docker Compose” as the build pack
- Coolify detects your
docker-compose.ymland deploys all services - Configure environment variables and domain in the Coolify dashboard
# Or deploy manually via SSHssh my-server "cd /opt/cruz-app && git pull && docker compose up -d --build"Deployment to Kubernetes
Section titled “Deployment to Kubernetes”Deployment Manifest
Section titled “Deployment Manifest”apiVersion: apps/v1kind: Deploymentmetadata: name: cruz-appspec: replicas: 2 selector: matchLabels: app: cruz-app template: metadata: labels: app: cruz-app spec: containers: - name: cruz-app image: myregistry/cruz-app:latest ports: - containerPort: 3000 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: cruz-secrets key: database-url - name: AUTH_SECRET valueFrom: secretKeyRef: name: cruz-secrets key: auth-secret - name: REDIS_URL value: redis://redis-service:6379 livenessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 10 readinessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 5 resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m"---apiVersion: v1kind: Servicemetadata: name: cruz-appspec: selector: app: cruz-app ports: - port: 80 targetPort: 3000 type: ClusterIPMigration Job
Section titled “Migration Job”Run migrations as a Kubernetes Job before deploying the new version:
apiVersion: batch/v1kind: Jobmetadata: name: cruz-migratespec: template: spec: containers: - name: migrate image: myregistry/cruz-app:latest command: ["npx", "cruz", "db", "migrate"] env: - name: DATABASE_URL valueFrom: secretKeyRef: name: cruz-secrets key: database-url restartPolicy: Never backoffLimit: 3Runtime Type
Section titled “Runtime Type”container — Long-running process. waitUntil() is fire-and-forget. Background jobs process in the same process via BullMQ (Redis-backed).