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 . migrate: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 . migrate: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 . migrate:run

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

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

Environment Variable Reference ​

General ​

VariableRequiredDefaultDescription
APP_ENVYes—Environment name (use production for deployed instances)

Authentication ​

VariableRequiredDefaultDescription
AUTH_JWT_SECRETYes—Secret key for JWT token signing

MySQL ​

VariableRequiredDefaultDescription
MYSQL_HOSTYes—MySQL server hostname
MYSQL_PORTNo3306MySQL server port
MYSQL_USERYes—MySQL username
MYSQL_PASSWORD_SECRETYes—MySQL password
MYSQL_DATABASEYes—MySQL 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_ENDPOINTYes—S3 endpoint URL
S3_REGIONYes—S3 region
S3_ACCESS_KEY_IDYes—S3 access key
S3_ACCESS_SECRETYes—S3 secret key
S3_BUCKETYes—S3 bucket name

Workers ​

VariableRequiredDefaultDescription
ENABLE_JOB_RUNNERNofalseEnable the job runner on the server process

Access Control ​

By default, PixelCI filters app reads (both the web UI and the /api/ext API) by whether the requesting user or token can access the app's project in the VCS provider — so users only see the apps their GitLab account can access.

VariableRequiredDefaultDescription
ENFORCE_VCS_PROJECT_ACCESSNotrueGate app reads by VCS project access. Set false to make every authenticated user able to read every app (e.g. for environments without a reachable VCS).

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 . migrate:run

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