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.
Docker Compose (Recommended)
The simplest way to run Checkgate is with the provided docker-compose.yml:
docker compose up -dThis 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:
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 developmentThen run:
docker compose up -dDocker Images
One image is published per release:
| Image | Description |
|---|---|
ghcr.io/thinkgrid-labs/checkgate:latest | All-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
| Variable | Default | Description |
|---|---|---|
SESSION_SECRET | (insecure dev key) | Secret for encrypting session cookies. Use a random ≥32-char string in production. |
DATABASE_URL | postgres://checkgate:password@localhost/checkgate | PostgreSQL connection string (used when not using bundled PG) |
REDIS_URL | redis://localhost:6379 | Redis connection string (used when not using bundled Redis) |
POSTGRES_PASSWORD | checkgate | Password for the bundled PostgreSQL instance |
POSTGRES_USER | checkgate | Username for the bundled PostgreSQL instance |
POSTGRES_DB | checkgate | Database name for the bundled PostgreSQL instance |
COOKIE_SECURE | true | Set 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_DIR | public | Directory to serve dashboard static files from |
PORT | 3000 | Port the server listens on |
DB_MAX_CONNECTIONS | 10 | PostgreSQL connection pool size |
RUST_LOG | info | Log 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.
# Generate a secure session secret
openssl rand -hex 32SDK 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:
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:latestAWS
EC2 Deployment
- Launch an EC2 instance (t3.micro is sufficient for most workloads)
- Install Docker:bash
sudo yum update -y && sudo yum install -y docker sudo systemctl start docker - Set up RDS PostgreSQL and ElastiCache Redis (optional — use the bundled versions for simpler setups)
- Create a
.envfile:bashDATABASE_URL=postgres://user:pass@rds-endpoint:5432/checkgate REDIS_URL=redis://elasticache-endpoint:6379 SESSION_SECRET=your-random-secret COOKIE_SECURE=true - Run:bash
docker run -d --env-file .env -p 3000:3000 \ ghcr.io/thinkgrid-labs/checkgate:latest
ECS (Fargate)
A basic task definition:
{
"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:
GET /healthReturns 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:
docker pull ghcr.io/thinkgrid-labs/checkgate:latest
docker compose up -d --force-recreateThe server applies any new migrations before accepting traffic. Existing data is preserved.
Backup
Back up your PostgreSQL database using standard tooling:
pg_dump -h localhost -U checkgate checkgate > checkgate-backup.sqlRedis is used only for pub/sub message passing and does not need to be backed up — no durable state is stored there.
