Performance Optimization
Optimize FraiseQL for production load with query tuning and caching. Performance Guide
This guide covers deploying FraiseQL to production environments, including Docker, cloud platforms, and operational best practices.
[project]name = "my-api"version = "1.0.0"
[server]host = "0.0.0.0"port = 8080
[server.cors]origins = ["https://app.example.com"]credentials = true
[database]url = "${DATABASE_URL}"pool_min = 10pool_max = 100connect_timeout_ms = 5000idle_timeout_ms = 600000ssl_mode = "require"
[security.error_sanitization]enabled = truehide_implementation_details = truesanitize_database_errors = true
[security.rate_limiting]enabled = truerequests_per_second = 100burst_size = 200JWT authentication is configured via environment variables, not fraiseql.toml (see Environment Variables below). There is no [auth] TOML section.
Required environment variables for production:
# DatabaseDATABASE_URL=postgresql://user:pass@host:5432/dbname?sslmode=require
# JWT authenticationJWT_SECRET=your-256-bit-secret-key # required if using JWT authJWT_ALGORITHM=HS256 # HS256 | RS256 | ES256 (default: HS256)
# Logging (standard Rust env var)RUST_LOG=info
# OpenTelemetry tracing (standard OTel env vars)OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.example.com:4317OTEL_SERVICE_NAME=fraiseql-apiFraiseQL is distributed as a pre-built Docker image — no Rust toolchain required. Use the official image as your base and copy in your schema:
FROM ghcr.io/fraiseql/fraiseql:latest
WORKDIR /app
# Copy schema source and configCOPY fraiseql.toml .COPY schema.py .
# Health checkHEALTHCHECK --interval=30s --timeout=3s --start-period=5s \ CMD wget -qO- http://localhost:8080/health || exit 1
EXPOSE 8080CMD ["fraiseql", "run"]For CI/CD pipelines where you want to pre-compile the schema to a static artifact:
# Stage 1: compile schema to schema.compiled.jsonFROM ghcr.io/fraiseql/fraiseql:latest AS builderWORKDIR /appCOPY fraiseql.toml .COPY schema.py .RUN fraiseql compile
# Stage 2: minimal runtime image with compiled schemaFROM ghcr.io/fraiseql/fraiseql:latestWORKDIR /appCOPY --from=builder /app/schema.compiled.json .COPY fraiseql.toml .
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \ CMD wget -qO- http://localhost:8080/health || exit 1
EXPOSE 8080CMD ["fraiseql", "run", "schema.compiled.json"]version: "3.8"
services: api: build: . ports: - "8080:8080" environment: - DATABASE_URL=${DATABASE_URL} - JWT_SECRET=${JWT_SECRET} - RUST_LOG=info depends_on: - postgres deploy: replicas: 3 resources: limits: cpus: "2" memory: 1G reservations: cpus: "0.5" memory: 256M healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"] interval: 30s timeout: 3s retries: 3
postgres: image: postgres:16-alpine environment: POSTGRES_DB: mydb POSTGRES_USER: user POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d mydb"] interval: 10s timeout: 5s retries: 5
volumes: postgres_data:apiVersion: apps/v1kind: Deploymentmetadata: name: fraiseql-api labels: app: fraiseql-apispec: replicas: 3 selector: matchLabels: app: fraiseql-api template: metadata: labels: app: fraiseql-api spec: containers: - name: api image: your-registry/fraiseql-api:latest ports: - containerPort: 8080 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: fraiseql-secrets key: database-url - name: JWT_SECRET valueFrom: secretKeyRef: name: fraiseql-secrets key: jwt-secret resources: requests: cpu: "500m" memory: "256Mi" limits: cpu: "2000m" memory: "1Gi" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10---apiVersion: v1kind: Servicemetadata: name: fraiseql-apispec: selector: app: fraiseql-api ports: - port: 80 targetPort: 8080 type: ClusterIP---apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: fraiseql-api annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prodspec: tls: - hosts: - api.example.com secretName: fraiseql-tls rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: fraiseql-api port: number: 80apiVersion: v1kind: Secretmetadata: name: fraiseql-secretstype: OpaquestringData: database-url: postgresql://user:pass@postgres:5432/mydb jwt-secret: your-256-bit-secret # injected as JWT_SECRET env varTask Definition:
{ "family": "fraiseql-api", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "1024", "memory": "2048", "containerDefinitions": [ { "name": "api", "image": "your-ecr/fraiseql-api:latest", "portMappings": [ { "containerPort": 8080, "protocol": "tcp" } ], "environment": [ { "name": "RUST_LOG", "value": "info" } ], "secrets": [ { "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:region:account:secret:fraiseql/database-url" }, { "name": "JWT_SECRET", "valueFrom": "arn:aws:secretsmanager:region:account:secret:fraiseql/jwt-secret" } ], "healthCheck": { "command": ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"], "interval": 30, "timeout": 5, "retries": 3 }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/fraiseql-api", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "ecs" } } } ]}apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: fraiseql-apispec: template: metadata: annotations: autoscaling.knative.dev/minScale: "1" autoscaling.knative.dev/maxScale: "10" spec: containerConcurrency: 100 containers: - image: gcr.io/your-project/fraiseql-api:latest ports: - containerPort: 8080 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: fraiseql-secrets key: database-url resources: limits: cpu: "2" memory: 1GiDeploy:
gcloud run services replace service.yaml --region=us-central1app = "fraiseql-api"primary_region = "iad"
[build]dockerfile = "Dockerfile"
[http_service]internal_port = 8080force_https = trueauto_stop_machines = falseauto_start_machines = truemin_machines_running = 2
[http_service.concurrency]type = "requests"soft_limit = 200hard_limit = 250
[[services.http_checks]]interval = "30s"timeout = "5s"path = "/health"
[[vm]]cpu_kind = "shared"cpus = 2memory_mb = 1024Deploy:
fly secrets set DATABASE_URL="..." JWT_SECRET="..."fly deploy# Create backuppg_dump $DATABASE_URL > backup-$(date +%Y%m%d).sql
# Run migrations (use your SQL migration tool: Flyway, Liquibase, sqitch, golang-migrate, etc.)# FraiseQL does not bundle a migration runner — manage schema changes separatelyFor zero-downtime deployments:
FraiseQL exposes health endpoints:
| Endpoint | Description |
|---|---|
/health | Overall health status |
/health/live | Liveness probe (is process running) |
/health/ready | Readiness probe (can accept traffic) |
curl http://localhost:8080/health# {"status": "healthy", "database": "connected", "version": "1.0.0"}FraiseQL is stateless and scales horizontally:
# Kubernetes HPAapiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: fraiseql-apispec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: fraiseql-api minReplicas: 3 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80For high-scale deployments, use external connection pooling:
# PgBouncer sidecar- name: pgbouncer image: edoburu/pgbouncer:1.21.0 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: fraiseql-secrets key: database-url - name: POOL_MODE value: transaction - name: MAX_CLIENT_CONN value: "1000" - name: DEFAULT_POOL_SIZE value: "20"[server.tls])JWT_SECRET env var, 256+ bits)[security.rate_limiting] in fraiseql.toml)[security.error_sanitization] in fraiseql.toml)[server.cors] in fraiseql.toml)# ServiceMonitor for Prometheus OperatorapiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: fraiseql-apispec: selector: matchLabels: app: fraiseql-api endpoints: - port: http path: /metrics interval: 30s| Metric | Description | Alert Threshold |
|---|---|---|
fraiseql_requests_total | Total requests | - |
fraiseql_request_duration_seconds | Request latency | p99 > 500ms |
fraiseql_db_pool_size | Connection pool size | > 80% of max |
fraiseql_errors_total | Error count | > 10/min |
Performance Optimization
Optimize FraiseQL for production load with query tuning and caching. Performance Guide
Troubleshooting
Debug production issues including connection errors and slow queries. Troubleshooting Guide
Security Hardening
Additional security configuration for production deployments. Security Guide
Cloud Deployment Guides
Step-by-step guides for AWS, GCP, and Azure deployments. Deployment Overview