Self-hosting
OtaKit is fully open source and can run on your own infrastructure. The managed service at otakit.app runs everything for you — self-hosting is the advanced path.
What you deploy
- Public site (
packages/site) — landing page, docs, contact, and legal pages. - Console (
packages/console) — Next.js control plane: auth, dashboard UI, API routes, billing, and Prisma migrations. - Ingest Worker (
packages/ingest) — Cloudflare Worker that receives device events and writes them to Tinybird. Required if you want to use dashboard analytics. - CDN bucket — public R2 or S3 bucket with a CDN domain. Serves manifest files and bundle zips directly to devices.
The CLI (packages/cli) and Capacitor plugin (packages/capacitor-plugin) are client-side tools — they can be configured to point at your self-hosted services.
Required services
- PostgreSQL 14+
- S3-compatible object storage (Cloudflare R2, AWS S3)
- A public CDN domain in front of the storage bucket
- At least one provider (Google, Apple, Github, or Email OTP via Resend) for sign-in
Environment variables
Dashboard — required
DATABASE_URL=postgresql://user:pass@localhost:5432/otakit BETTER_AUTH_SECRET=your-random-secret # openssl rand -hex 32 BETTER_AUTH_URL=https://your-domain.com # At least one provider for sign-in GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=.... RESEND_API_KEY=... EMAIL_FROM=... R2_BUCKET=... R2_ACCESS_KEY=... R2_SECRET_KEY=... R2_ENDPOINT=https://....r2.cloudflarestorage.com CDN_BASE_URL=https://cdn.your-domain.com # Cloudflare CDN purge — instant cache invalidation after releases. # Without this, stale manifests may be served until CDN TTL expires. CF_ZONE_ID=... CF_API_TOKEN=... # Tinybird — device event analytics and dashboard counts. # Without this, the dashboard shows empty analytics and download counts return 0. TINYBIRD_API_HOST=https://api.tinybird.co TINYBIRD_READ_TOKEN=... # Manifest signing — ES256 signatures on manifest JSON. # Generate with: otakit generate-signing-key MANIFEST_SIGNING_KID=key-2026-01 MANIFEST_SIGNING_KEY=-----BEGIN EC PRIVATE KEY-----... # Set MANIFEST_SIGNING_DISABLED=true to skip signing entirely.
Ingest Worker
Only needed if you want to use analytics. See packages/ingest/wrangler.jsonc and packages/ingest/.env.example for the full config. The Worker needs a Tinybird append token and a Cloudflare Queue.
Deploy
Public site
git clone https://github.com/OtaKit/otakit cd otakit pnpm install cd packages/site pnpm build pnpm start
Console
git clone https://github.com/OtaKit/otakit cd otakit pnpm install cd packages/console npx prisma migrate deploy pnpm build pnpm start
Runs on port 3000. Put a reverse proxy (nginx, Caddy) in front with HTTPS.
Ingest Worker
cd packages/ingest npx wrangler deploy
Tinybird project
cd tinybird tb login tb deploy
Manifest signing
otakit generate-signing-key
Add the private key to the dashboard env (MANIFEST_SIGNING_KID, MANIFEST_SIGNING_KEY). Add the public key to the Capacitor plugin config (manifestKeys).
Configure the plugin
plugins: {
OtaKit: {
appId: "YOUR_OTAKIT_APP_ID",
cdnUrl: "https://cdn.your-domain.com",
ingestUrl: "https://ingest.your-domain.com/v1", // omit if not using Tinybird
manifestKeys: [
{ kid: "key-2026-01", key: "MFkwEwYH..." }
]
}
}Configure the CLI
export OTAKIT_SERVER_URL=https://your-domain.com/api/v1 export OTAKIT_TOKEN=otakit_sk_...
Or set serverUrl in the Capacitor plugin config so the CLI can read it automatically.