Skip to content

Deployment Guide

This guide covers self-hosting PixelCI — the API/UI server, background workers, and required infrastructure.

Prerequisites

  • MySQL 8+ — stores apps, builds, branches, screens, and user data
  • Redis — job queue (BullMQ) and caching
  • S3-compatible storage — screenshots and diff images (AWS S3, MinIO, SeaweedFS, etc.)

Docker Quick Start

1. Start the server

bash
docker run -d \
  --name pixelci \
  -p 7924:7924 \
  -e APP_ENV=production \
  -e ENABLE_JOB_RUNNER=true \
  -e AUTH_JWT_SECRET=your-secret-key \
  -e REDIS_HOST=redis \
  -e REDIS_PREFIX=pixelci \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD_SECRET=your-password \
  -e MYSQL_DATABASE=pixelci \
  -e S3_ENDPOINT=https://s3.amazonaws.com \
  -e S3_REGION=us-east-1 \
  -e S3_ACCESS_KEY_ID=your-access-key \
  -e S3_ACCESS_SECRET=your-secret-key \
  -e S3_BUCKET=pixelci \
  ghcr.io/zyno-io/pixelci:latest

Setting ENABLE_JOB_RUNNER=true makes the server also process build comparison jobs. For production deployments with higher throughput, you can run a separate worker process instead.

2. Run database migrations

bash
docker exec pixelci node . migration:run

3. Access the web interface

Visit http://localhost:7924. On first launch, the onboarding wizard will guide you through creating your first VCS integration and logging in.

Docker Compose

A complete setup with MySQL, Redis, and PixelCI:

yaml
services:
    pixelci:
        image: ghcr.io/zyno-io/pixelci:latest
        ports:
            - '7924:7924'
        environment:
            APP_ENV: production
            ENABLE_JOB_RUNNER: 'true'
            AUTH_JWT_SECRET: your-secret-key
            REDIS_HOST: redis
            REDIS_PREFIX: pixelci
            MYSQL_HOST: mysql
            MYSQL_USER: root
            MYSQL_PASSWORD_SECRET: your-password
            MYSQL_DATABASE: pixelci
            S3_ENDPOINT: https://s3.amazonaws.com
            S3_REGION: us-east-1
            S3_ACCESS_KEY_ID: your-access-key
            S3_ACCESS_SECRET: your-secret-key
            S3_BUCKET: pixelci
        depends_on:
            mysql:
                condition: service_healthy
            redis:
                condition: service_healthy

    mysql:
        image: mysql:8
        environment:
            MYSQL_ROOT_PASSWORD: your-password
            MYSQL_DATABASE: pixelci
        volumes:
            - mysql-data:/var/lib/mysql
        healthcheck:
            test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-pyour-password']
            interval: 5s
            timeout: 3s
            retries: 10

    redis:
        image: redis:7-alpine
        healthcheck:
            test: ['CMD', 'redis-cli', 'ping']
            interval: 5s
            timeout: 3s
            retries: 10

volumes:
    mysql-data:

After starting:

bash
docker compose up -d
docker compose exec pixelci node . migration:run

Then visit http://localhost:7924. The onboarding wizard will guide you through initial setup.

Running Migrations

Migrations must be run whenever you deploy a new version:

bash
# Docker
docker exec pixelci node . migration:run

# Docker Compose
docker compose exec pixelci node . migration:run

# Kubernetes
kubectl exec deploy/pixelci-server -- node . migration:run

Environment Variable Reference

General

VariableRequiredDefaultDescription
APP_ENVYesEnvironment name (use production for deployed instances)

Authentication

VariableRequiredDefaultDescription
AUTH_JWT_SECRETYesSecret key for JWT token signing

MySQL

VariableRequiredDefaultDescription
MYSQL_HOSTYesMySQL server hostname
MYSQL_PORTNo3306MySQL server port
MYSQL_USERYesMySQL username
MYSQL_PASSWORD_SECRETYesMySQL password
MYSQL_DATABASEYesMySQL database name

Redis

VariableRequiredDefaultDescription
REDIS_HOSTYes*Redis server hostname
REDIS_PORTNo6379Redis server port
REDIS_PREFIXNopackage namePrefix for all Redis keys (recommended: pixelci)
REDIS_SENTINEL_HOSTYes*Redis Sentinel hostname (alternative to REDIS_HOST)
REDIS_SENTINEL_PORTNo26379Redis Sentinel port
REDIS_SENTINEL_NAMEYes*Redis Sentinel master name (required with sentinel)

* Provide either REDIS_HOST or REDIS_SENTINEL_HOST + REDIS_SENTINEL_NAME.

S3 Storage

VariableRequiredDefaultDescription
S3_ENDPOINTYesS3 endpoint URL
S3_REGIONYesS3 region
S3_ACCESS_KEY_IDYesS3 access key
S3_ACCESS_SECRETYesS3 secret key
S3_BUCKETYesS3 bucket name

Workers

VariableRequiredDefaultDescription
ENABLE_JOB_RUNNERNofalseEnable the job runner on the server process

Tuning

VariableRequiredDefaultDescription
DEFAULT_PIXEL_MATCH_THRESHOLDNo0.2Per-pixel color sensitivity for comparison (0–1, smaller = more sensitive). Higher values reduce false positives from sub-pixel text rendering and anti-aliasing differences.
DEFAULT_PIXEL_DIFF_PCT_THRESHOLDNo1Percentage of total pixels that must differ to flag a screen as changed (e.g. 0.5 for 0.5%)

Architecture Overview

PixelCI uses a single Docker image (ghcr.io/zyno-io/pixelci) for all components. Build comparison jobs are processed by a worker, which can run either within the server process or as a separate process.

Single-process mode

Set ENABLE_JOB_RUNNER=true on the server. The server handles both HTTP requests and background jobs. This is the simplest setup and works well for most teams.

Separate worker process

For higher throughput or to isolate job processing from the API, run the worker as a separate container using the same image with a different command:

bash
docker run -d \
  --name pixelci-worker \
  -e APP_ENV=production \
  -e AUTH_JWT_SECRET=your-secret-key \
  -e REDIS_HOST=redis \
  -e REDIS_PREFIX=pixelci \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD_SECRET=your-password \
  -e MYSQL_DATABASE=pixelci \
  -e S3_ENDPOINT=https://s3.amazonaws.com \
  -e S3_REGION=us-east-1 \
  -e S3_ACCESS_KEY_ID=your-access-key \
  -e S3_ACCESS_SECRET=your-secret-key \
  -e S3_BUCKET=pixelci \
  ghcr.io/zyno-io/pixelci:latest \
  node . worker:start

When using a separate worker, do not set ENABLE_JOB_RUNNER on the server.

Kubernetes

For Kubernetes deployments, you can either:

  • Single-process: One Deployment with ENABLE_JOB_RUNNER=true, exposed via a Service/Ingress on port 7924
  • Separate worker: Two Deployments sharing the same image — the server (default command) and a worker (override command to node . worker:start)

Run migrations via kubectl exec against the server deployment after each upgrade:

bash
kubectl exec deploy/pixelci-server -- node . migration:run

All deployments share the same environment variables. Use Kubernetes Secrets for sensitive values (AUTH_JWT_SECRET, MYSQL_PASSWORD_SECRET, S3 credentials).