Skip to content

Self-Hosting

Checkgate is designed to be self-hosted. The all-in-one Docker image bundles PostgreSQL, Redis, the server, and the dashboard into a single container — no external services required.

The simplest way to run Checkgate is with the provided docker-compose.yml:

bash
docker compose up -d

This starts a single container that includes:

  • PostgreSQL — persistent flag and user storage
  • Redis — pub/sub for real-time multi-instance sync
  • Checkgate server — REST API + SSE stream on port 3000
  • React dashboard — served at http://localhost:3000

Configuration

Create a .env file in the project root:

bash
SESSION_SECRET=your-random-64-char-secret   # Required in production
POSTGRES_PASSWORD=strong-db-password        # Replaces default "checkgate"
COOKIE_SECURE=true                          # Set false only for local HTTP development

Then run:

bash
docker compose up -d

Docker Images

One image is published per release:

ImageDescription
ghcr.io/thinkgrid-labs/checkgate:latestAll-in-one: PostgreSQL + Redis + server + dashboard
ghcr.io/thinkgrid-labs/checkgate:<version>Pinned version (e.g. 1.2.0)

The image is multi-architecture: linux/amd64 and linux/arm64.

Environment Variables

VariableDefaultDescription
SESSION_SECRET(insecure dev key)Secret for encrypting session cookies. Use a random ≥32-char string in production.
DATABASE_URLpostgres://checkgate:password@localhost/checkgatePostgreSQL connection string (used when not using bundled PG)
REDIS_URLredis://localhost:6379Redis connection string (used when not using bundled Redis)
POSTGRES_PASSWORDcheckgatePassword for the bundled PostgreSQL instance
POSTGRES_USERcheckgateUsername for the bundled PostgreSQL instance
POSTGRES_DBcheckgateDatabase name for the bundled PostgreSQL instance
COOKIE_SECUREtrueSet false for local HTTP development. Controls the Secure cookie flag.
SDK_KEY(unset)Legacy env var: adds an extra accepted SDK key without a DB entry
PUBLIC_DIRpublicDirectory to serve dashboard static files from
PORT3000Port the server listens on
DB_MAX_CONNECTIONS10PostgreSQL connection pool size
RUST_LOGinfoLog level (error, warn, info, debug, trace)

Security

Always set SESSION_SECRET in production. Without it, session cookies use a hardcoded dev key that is public knowledge — anyone can forge sessions.

bash
# Generate a secure session secret
openssl rand -hex 32

SDK keys are managed through the dashboard Settings page. The first key is auto-generated on first boot. Additional keys can be created and revoked without restarting the server.

External PostgreSQL and Redis

To use your own PostgreSQL and Redis instead of the bundled ones, run the image directly and pass the connection strings:

bash
docker run -d -p 3000:3000 \
  -e DATABASE_URL="postgres://user:password@your-postgres:5432/checkgate" \
  -e REDIS_URL="redis://your-redis:6379" \
  -e SESSION_SECRET="your-secret" \
  ghcr.io/thinkgrid-labs/checkgate:latest

AWS

EC2 Deployment

  1. Launch an EC2 instance (t3.micro is sufficient for most workloads)
  2. Install Docker:
    bash
    sudo yum update -y && sudo yum install -y docker
    sudo systemctl start docker
  3. Set up RDS PostgreSQL and ElastiCache Redis (optional — use the bundled versions for simpler setups)
  4. Create a .env file:
    bash
    DATABASE_URL=postgres://user:pass@rds-endpoint:5432/checkgate
    REDIS_URL=redis://elasticache-endpoint:6379
    SESSION_SECRET=your-random-secret
    COOKIE_SECURE=true
  5. Run:
    bash
    docker run -d --env-file .env -p 3000:3000 \
      ghcr.io/thinkgrid-labs/checkgate:latest

ECS (Fargate)

A basic task definition:

json
{
  "family": "checkgate",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "containerDefinitions": [
    {
      "name": "checkgate",
      "image": "ghcr.io/thinkgrid-labs/checkgate:latest",
      "portMappings": [{"containerPort": 3000}],
      "environment": [
        {"name": "DATABASE_URL", "value": "postgres://..."},
        {"name": "REDIS_URL", "value": "redis://..."},
        {"name": "SESSION_SECRET", "value": "your-random-secret"},
        {"name": "COOKIE_SECURE", "value": "true"}
      ]
    }
  ]
}

Load Balancing

Checkgate is stateless — the in-memory flag store is rebuilt from PostgreSQL on startup and kept in sync via Redis pub/sub. Run as many instances as you need behind an ALB.

Important: The SSE stream (/stream) requires sticky sessions or a long-lived HTTP/2 connection. Configure your ALB target group with:

  • Protocol: HTTP/1.1 (SSE is not compatible with HTTP/2 connection multiplexing on ALB)
  • Stickiness: Enabled (duration-based, any duration)

Or rely on Redis pub/sub fan-out (built-in): all instances subscribe to Redis and will forward updates to their connected SDK clients regardless of which instance received the API write.

Health Checks

The server exposes a health check endpoint:

http
GET /health

Returns 200 OK with body OK. Use this for load balancer and container health checks (as configured in the Dockerfile's HEALTHCHECK instruction).

Upgrading

Checkgate uses sqlx migrations that run automatically on startup. To upgrade:

bash
docker pull ghcr.io/thinkgrid-labs/checkgate:latest
docker compose up -d --force-recreate

The server applies any new migrations before accepting traffic. Existing data is preserved.

Backup

Back up your PostgreSQL database using standard tooling:

bash
pg_dump -h localhost -U checkgate checkgate > checkgate-backup.sql

Redis is used only for pub/sub message passing and does not need to be backed up — no durable state is stored there.

Released under the MIT License.