Skip to content

Deploy to Vercel

The scaffolded app is already shaped for Vercel. Deployment mainly means linking the project, keeping juniorNitro() in Nitro config, setting env vars, enabling snapshot warmup support, and pointing Slack at the production URL.

Authenticate and link the local app to a Vercel project:

Terminal window
pnpm dlx vercel@latest login
pnpm dlx vercel@latest link

If your account requires a team scope, pass the same --scope <team-slug> value to Vercel commands.

The scaffolded package.json includes the production build script:

package.json
{
"scripts": {
"check": "junior check",
"dev": "vite dev",
"build": "junior snapshot create && vite build"
}
}

Keep the Vercel build command as pnpm build. junior snapshot create prepares sandbox runtime dependencies declared by enabled plugins before request handling starts.

Junior uses a one-minute internal heartbeat to run plugin heartbeats and recover stale agent dispatches. Durable agent work is also resumed by a Vercel Queue consumer. Both pieces are emitted by juniorNitro() into Nitro’s Vercel Build Output config, which is the config Vercel deploys for Nitro apps.

Keep juniorNitro() installed in nitro.config.ts:

nitro.config.ts
import { defineConfig } from "nitro";
import { juniorNitro } from "@sentry/junior/nitro";
export default defineConfig({
preset: "vercel",
modules: [juniorNitro()],
routes: {
"/**": { handler: "./server.ts" },
},
});

Do not configure functions["api/internal/agent/continue.ts"] in root vercel.json; Nitro does not deploy that source file as a Vercel function. juniorNitro() attaches the queue trigger to /api/internal/agent/continue with Nitro vercel.functionRules, and emits the /api/internal/heartbeat cron into .vercel/output/config.json.

The heartbeat endpoint returns 401 unless the incoming Vercel Cron request has a bearer token that matches CRON_SECRET.

Set the core runtime variables in Vercel:

VariableRequiredPurpose
SLACK_SIGNING_SECRETYesVerifies Slack requests.
SLACK_BOT_TOKEN or SLACK_BOT_USER_TOKENYesPosts replies and calls Slack APIs.
REDIS_URLYesQueue and runtime state storage.
JUNIOR_SECRETYesSigns internal callbacks and sandbox requester context.
CRON_SECRETYesAuthenticates Vercel Cron requests to the internal heartbeat route.
JUNIOR_BASE_URLConditionalCanonical URL for OAuth and callback URLs when Vercel URL envs are not enough.
JUNIOR_STATE_KEY_PREFIXNoRedis key namespace for this deployment when sharing one Redis database.
AI_GATEWAY_API_KEYOptionalAI Gateway auth when your setup requires it.

Use one stable JUNIOR_SECRET per deployment:

Terminal window
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))"

Plugin pages list provider-specific env vars such as GitHub App settings or Datadog keys.

Generate CRON_SECRET the same way and store it in Vercel for the production environment. Vercel Cron automatically sends it to cron targets as the Authorization: Bearer <CRON_SECRET> header.

If enabled plugins need sandbox runtime dependencies, junior snapshot create runs during build. In Vercel, enable OIDC so VERCEL_OIDC_TOKEN is available during the build.

Snapshot warmup also needs REDIS_URL during build because the snapshot registry is Redis-backed.

Update these Slack URLs to your production domain:

https://<your-domain>/api/webhooks/slack

Apply the URL to:

  • Event Subscriptions
  • Interactivity
  • Slash command configured by JUNIOR_SLASH_COMMAND (defaults to /jr)

Reinstall the Slack app if scopes changed.

Run these checks after deployment:

  1. GET https://<your-domain>/health returns status: "ok".
  2. junior check passes without deployment config errors.
  3. The Vercel deployment has a cron entry for /api/internal/heartbeat.
  4. The Vercel deployment has a Queue trigger for /api/internal/agent/continue.
  5. A Slack mention produces a thread reply in the expected workspace.
  6. App Home opens without an error.
  7. Queue callback and agent-run logs show successful processing.
  8. One enabled plugin workflow succeeds end to end.

Use Verify & Troubleshoot for first-response checks, then monitor production with Observability.