The Docker Compose stack is the fastest path to a working Thunderbolt install. It's well suited to demos, evaluations, and single-host internal tools.
- Docker 24+ with the Compose plugin (
docker compose versionmust succeed) - 4 GB RAM minimum, 8 GB recommended
- Ports
3000,5434,8081,8180available (or editdeploy/.envto remap —FRONTEND_PORT,POSTGRES_PORT,POWERSYNC_PORT,KEYCLOAK_PORT)
git clone https://github.com/thunderbird/thunderbolt.git
cd thunderbolt/deploy
cp .env.example .env
# Edit .env — at minimum set BETTER_AUTH_SECRET (32+ chars; generate with
# `openssl rand -base64 32`) and one AI provider API key.
# Note: POWERSYNC_JWT_SECRET is hardcoded in docker-compose.yml; override
# only if you fork the compose file.
docker compose up --buildThe backend entrypoint runs Drizzle migrations before serving traffic, the Keycloak realm imports on first boot, and PowerSync loads its sync rules from deploy/config/powersync-config.yaml.
| Service | URL | Credentials |
|---|---|---|
| App | http://localhost:3000 |
Keycloak SSO (demo user below) |
| Keycloak admin | http://localhost:8180 |
admin / admin — rotate immediately |
| Demo user | (sign in via app) | demo@thunderbolt.io / demo |
Behind the scenes, the compose file boots:
| Dockerfile | Base | Purpose |
|---|---|---|
docker/frontend.Dockerfile |
oven/bun → nginx:alpine |
Vite SPA with COEP/COOP headers |
docker/backend.Dockerfile |
oven/bun:latest |
Elysia API; entrypoint runs bun drizzle-kit migrate before starting |
docker/postgres.Dockerfile |
postgres:18-alpine |
PostgreSQL with PowerSync replication role (deploy/docker/postgres-init/01-powersync.sh) |
docker/keycloak.Dockerfile |
keycloak:26.0 |
OIDC/SAML with the thunderbolt realm pre-imported |
docker/powersync.Dockerfile |
journeyapps/powersync-service |
PowerSync service with the synced-table rules; also stores its bucket data in Postgres (powersync_storage DB) |
- Bring your own identity provider. Remove the
keycloakservice from the compose file, then set the OIDC vars (OIDC_ISSUER,OIDC_CLIENT_ID,OIDC_CLIENT_SECRET) or the SAML vars (SAML_ENTRY_POINT,SAML_ENTITY_ID,SAML_IDP_ISSUER,SAML_CERT) in.envdepending on yourAUTH_MODE. - Use managed Postgres. Point
DATABASE_URLat your Postgres, remove thepostgresservice, and manually rundeploy/docker/postgres-init/01-powersync.sqlagainst it to create thepowersync_roleuser and publication. - TLS. The bundled stack serves plain HTTP. Put it behind Caddy, Traefik, or the reverse proxy of your choice — the frontend nginx expects the upstream to terminate TLS.
cd thunderbolt
git pull
cd deploy
docker compose pull
docker compose up -d --buildThe backend entrypoint applies pending migrations on start. PowerSync reloads sync rules from config.yaml.
docker compose down # stop, keep data
docker compose down -v # stop + wipe Postgres, Mongo, and Keycloak data