Vercel Deployment skill for Codex
Previews, env scopes and one-command rollbacks. Codex reproduces Vercel builds locally with vercel pull and vercel build before blaming the platform, scopes every environment variable to Production, Preview or Development, and pins functions to the region of the database. It secures cron routes with CRON_SECRET, uses Edge Config only for small flags, deploys prebuilt artifacts from CI and asks before any production deploy, promote or rollback.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- vercel-deployment Installs to ~/.agents/skills/vercel-deployment/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install vercel-deployment The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project vercel-deployment Installs to .agents/skills/vercel-deployment/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/vercel-deployment.zipunzip -o vercel-deployment.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) vercel-deployment PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $vercel-deployment, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Deploys and operates projects on Vercel - vercel.json routing, environment variables per environment, Vercel Functions duration, memory and region, Fluid compute, cron jobs, Edge Config, preview deployments, prebuilt CI deploys, promote and rollback - using the vercel CLI. Use when a Vercel build fails, env vars are missing at runtime, a function times out, previews leak to the public, you are wiring GitHub Actions to deploy, or you need to roll back production fast.
Use it when
- A Vercel build or deployment fails or behaves differently from local
- Environment variables are missing or leak between preview and production
- Functions time out or are slow because of region or duration settings
- You need cron jobs, Edge Config, or CI-driven Vercel deploys
Use something else when
- The question is about Next.js caching and revalidation semantics: use nextjs-caching
- You are deciding whether code should run at the edge at all: use edge-functions
How to check the result
vercel build succeeds locally, a preview URL serves the change with the expected headers and env, and the rollback command is known before production is touched.
The mistake it prevents
Editing an environment variable and expecting the live deployment to pick it up; values are baked per deployment, so you must redeploy.
A healthy Vercel project builds the same way locally, in CI and on Vercel; every environment variable is scoped to the right environment; functions run in the region next to their database; and production can be rolled back in under a minute without a rebuild.
Inspect first
- Read
AGENTS.md,package.json, the lockfile andvercel.json(orvercel.ts). - Check
.vercel/project.jsonexists. If not, the directory is not linked:vercel linkneeds network and a logged-in CLI, so ask the user to run it or provideVERCEL_TOKEN. - Identify the framework. Next.js, SvelteKit, Nuxt, Astro and Remix configure functions
through the framework; plain
api/directories use Vercel Functions directly. - Note the monorepo root: the Vercel project’s Root Directory setting must point at the app package, and the install command must run from the workspace root.
Core CLI loop
vercel pull --yes --environment=preview # project settings + env into .vercel/
vercel env pull .env.local # development env vars for local runs
vercel build # reproduce the cloud build locally
vercel deploy # preview deployment, prints a unique URL
vercel deploy --prod # production: ask the user first
vercel inspect <deployment-url> --logs # build logs of one deployment
vercel logs <deployment-url> # runtime logs
vercel promote <deployment-url> # make an existing deployment production
vercel rollback # instant rollback to the previous production
Every command that talks to Vercel needs network. In CI, pass --token "$VERCEL_TOKEN"
and set VERCEL_ORG_ID and VERCEL_PROJECT_ID.
Environment variables
| Scope | Used by |
|---|---|
| Production | vercel deploy --prod and the production branch |
| Preview | Every other branch; can be narrowed to one branch |
| Development | vercel env pull and vercel dev only |
vercel env ls
vercel env add DATABASE_URL production # prompts for the value; mark it sensitive
vercel env add DATABASE_URL preview feature-x # branch-scoped preview value
vercel env rm OLD_KEY production
Rules:
- Changing a variable does not affect existing deployments. Redeploy after every change.
- Anything prefixed for the client (
NEXT_PUBLIC_,VITE_,PUBLIC_) is inlined into the bundle at build time and is public. Never put secrets there. - Preview deployments must not point at the production database. Give Preview its own
DATABASE_URLor a branch database from a Marketplace integration. - System variables worth using:
VERCEL_ENV(production, preview, development),VERCEL_URL(deployment host, no protocol),VERCEL_GIT_COMMIT_SHA.
vercel.json essentials
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"regions": ["iad1"],
"functions": {
"api/reports/*.ts": { "maxDuration": 120, "memory": 1769 }
},
"crons": [{ "path": "/api/cron/cleanup", "schedule": "0 3 * * *" }],
"redirects": [{ "source": "/docs/:path*", "destination": "https://docs.example.com/:path*", "permanent": true }],
"rewrites": [{ "source": "/app/(.*)", "destination": "/app/index.html" }],
"headers": [{
"source": "/assets/(.*)",
"headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
}],
"cleanUrls": true
}
regions: put functions next to the database. A function iniad1querying a database in Frankfurt adds roughly 90 ms per round trip; an ORM doing five queries pays it five times.maxDurationandmemorylimits depend on the plan and on whether Fluid compute is on (it is the default for new projects). Check the current plan limits instead of guessing; move anything longer than a few minutes to a queue or background worker.- Frameworks with their own routing (Next.js) should keep redirects in the framework
config so they work in
next devtoo.
Cron jobs
Vercel calls the cron path with GET on the production deployment only. Protect it:
export async function GET(req: Request) {
if (req.headers.get("authorization") !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response("unauthorized", { status: 401 });
}
await cleanup();
return Response.json({ ok: true });
}
Set CRON_SECRET in Production; Vercel sends it as a bearer token automatically. Make the
job idempotent: a cron can run twice or be skipped.
Edge Config
Use Edge Config for small, read-mostly values needed on every request (feature flags, redirect maps, IP blocklists). Reads are fast; writes go through the REST API or dashboard and propagate in seconds.
import { get } from "@vercel/edge-config"; // reads the EDGE_CONFIG connection string
export async function middleware() {
const maintenance = await get<boolean>("maintenance");
if (maintenance) return new Response("Down for maintenance", { status: 503 });
}
Do not use it as a database: it has a small total size and is not designed for frequent writes. For per-user data use a real store from the Marketplace (Postgres, Redis).
CI deploys with prebuilt output
Build once in CI, deploy the exact artifact:
- run: npm i -g vercel@latest
- run: vercel pull --yes --environment=production --token "$VERCEL_TOKEN"
- run: vercel build --prod --token "$VERCEL_TOKEN"
- run: vercel deploy --prebuilt --prod --token "$VERCEL_TOKEN"
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
When CI deploys, disable Git-triggered builds for the same branch ("git": {"deploymentEnabled": false}
or per branch) so each commit is not built twice.
Decision table
| Symptom | First check |
|---|---|
| Works locally, fails on Vercel build | Node version setting, lockfile committed, Root Directory, vercel build locally |
undefined env var at runtime | Scope (Preview vs Production), redeploy after change, client prefix |
| Function 504 / timeout | maxDuration, region vs database, slow cold path, long work in request |
| Preview visible to strangers | Deployment Protection settings; use the bypass secret for automated tests |
| Users get 404 for old JS chunks after deploy | Enable Skew Protection or keep old assets |
| Production broke after deploy | vercel rollback first, investigate second |
Verification
-
vercel buildsucceeds locally with pulled settings. - A preview deploy URL serves the change;
curl -Ishows expected headers and redirects. - Runtime logs show no missing-env errors; cron route returns 401 without the secret.
- Production deploys, promotes and rollbacks happen only after explicit user approval.
Gotchas
vercel devdoes not perfectly emulate every framework; trustvercel buildplus a preview deploy oververcel devfor routing questions.- Edge runtime functions cannot use most Node APIs or native modules; Vercel now recommends the Node.js runtime unless you need the edge specifically.
- Environment variables have a total size cap per deployment; large certificates or JSON blobs belong in a secret store or file, not in env.
vercel rollbackpoints the production domain at an older deployment, but env vars and databases do not roll back with it. Data migrations need their own reverse plan.
Output
## Vercel change: <summary>
- Project / root dir / framework:
- Env vars added or changed (name, scopes; never values):
- vercel.json changes:
- Preview URL tested:
- Production action taken (with user approval) or pending:
- Rollback path: vercel rollback / promote <url> What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Vercel Deployment"
short_description: "Previews, env scopes and one-command rollbacks"
default_prompt: "$vercel-deployment Our Vercel build passes locally but fails in the cloud, figure out why" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.