Skip to main content
FrogBot apps are Next.js applications. They build like a standard Next.js app, but two things differ from a plain Next.js project:
  1. The build must run in full, at image build time — not split into “compile in the image / generate at runtime”. FrogBot’s prebuild step generates the admin import map, and next build compiles the app. Running next build --experimental-build-mode generate at container start (as some platform templates do) re-runs webpack in production, which requires typescript at runtime and slows cold starts dramatically.
  2. In production, FrogBot does not push your schema automatically the way it does in dev. A fresh production database has no tables until migrations run — see Migrations.

Docker

The template ships a production Dockerfile based on the Next.js with-docker example:
  • Package-manager agnostic — it detects your committed lockfile (pnpm-lock.yaml, package-lock.json, or yarn.lock) and installs with the matching tool. The template ships no lockfile; your first install creates it.
  • Standalone output — the build stage sets NEXT_OUTPUT=standalone (wired up in the template’s next.config.mjs), and the final image contains only Next’s traced server: no package manager, no full node_modules. It starts with plain node server.js (~110 MB image, sub-second boot).
  • Build isolation — the build stage uses a throwaway DATABASE_URL and a placeholder FROGBOT_SECRET, so no local data or secrets are baked into the image. Real values come from environment variables at runtime.

Health check

The template answers GET /api/health with 200 and { "status": "ok" } once FrogBot has started, and 503 with { "status": "error" } when it can’t start, logging why. It needs no sign-in, so point your platform’s HTTP health check at it. Dojo, where the Dojo plugin’s frogbot share runs apps, checks this path. The route is src/app/(frogbot)/api/health/route.ts.

Fly.io

Fly’s generator detects the existing Dockerfile and uses it instead of generating its default Next.js two-phase build, so your app gets a fast, single-phase cold start. For a complete, working setup — persistent volume, migrations, scale-to-zero — see the fly-sqlite example.

Database persistence

The default SQLite database lives in a local frogbot.db file on the machine’s ephemeral storage. If your machines auto-stop (scale to zero), the data is wiped on restart. Choose one of:
  • Attach a persistent volume and point DATABASE_URL at it:
    A volume attaches to one machine at a time, so with SQLite you run a single machine (fly scale count 1). Scale-to-zero still works — the data lives on the volume, not the machine.
  • Use a remote database: Postgres, MongoDB, or a hosted SQLite like Turso. Set DATABASE_URL accordingly.

Migrations

In development, FrogBot pushes your schema to SQLite automatically on boot. In production it does not — it only applies migrations listed in prodMigrations. Before your first production deploy:
Then wire the migrations into your database adapter:
Commit the generated files. They are applied automatically on boot (before the server starts serving), so a fresh database gets its schema on first deploy. When you add or change collections, run pnpm migrate:create <name> again and commit the new files. Generated migrations import from the underlying database adapter package, which the template includes as a direct dependency — do not remove it.

Troubleshooting

no such table: users in production

Your production database has no schema. FrogBot only pushes schema automatically in dev; in production you need migrations wired into prodMigrations — see Migrations.

ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION during Docker build

Recent pnpm versions refuse packages published within the last 24 hours as a supply-chain protection. If a FrogBot release is newer than that, either wait, pin an older pnpm via the packageManager field in package.json, or relax the policy in pnpm-workspace.yaml (minimumReleaseAge: 0).

Cannot find module 'libsql' (or sharp, pino, drizzle-kit, json-schema-to-typescript)

Only applies to non-standalone deploys (bare VPS running pnpm start): the compiled routes require() a package that FrogBot externalizes from the webpack bundle, but it is not linked at your project root. Add it to dependencies in package.json and rebuild — the template already includes all of them. Standalone Docker images are unaffected: the build includes these packages, and libsql’s native binary for the platform the image is built on. Build the image with the template Dockerfile so dependencies install inside Linux. node_modules copied in from macOS or Windows has no Linux binary, and the server fails with Cannot find module '@libsql/linux-…'.

Installing TypeScript... or Can't resolve '@frogbot-config' at start

A runtime next build step is running (usually a platform template with a two-phase build), and typescript was pruned from the image. Use the template Dockerfile above so the build happens once, in the image, and start runs only node server.js.

Slow cold starts

If your platform starts a build at container boot, your cold start includes a webpack rebuild. Move the build into the image (see Docker) so cold starts are just the standalone server booting.

Installation

Scaffold a project and run it locally.

Database

Choose a database adapter that fits your production storage needs.