# Ekx Vercel Deploy

> Deploying and operating Ekinoxis apps on Vercel — project linking, environment variables per environment, preview vs production deploys, build failures, runtime logs and errors, cron jobs, and domain setup. Use when a deploy fails, when an app works locally but not in production, when adding or rotating an env var, when setting up a scheduled job, or when configuring a custom domain.

- Skill: `ekinoxis-evm/ekx-vercel-deploy` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ekinoxis-evm/ekx-vercel-deploy`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ekinoxis-evm/ekx-vercel-deploy/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: Ekinoxis-evm (https://skillmd.com/u/ekinoxis-evm)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/ekinoxis-evm/ekx-vercel-deploy

---


# Vercel

Every web surface we ship. 18 projects.

Authoritative: the **`vercel` MCP server** (in 5 of our repos) — use it to read build
logs, runtime errors and deployment status rather than asking the user to paste them.
Plus the bundled `vercel-cli` and `deployments-cicd` skills.

---

## CLI

```bash
vercel link                       # connect the local folder to a project
vercel env pull .env.local        # pull dev env vars down — do this first, always
vercel                            # preview deploy
vercel --prod                     # production
vercel logs <url>                 # runtime logs
vercel inspect <url>              # build output + timings
```

`vercel env pull` is the correct way to get a working `.env.local`. Copying from a
teammate's laptop is how the three different Supabase key names got into the portfolio.

---

## Environments

Three scopes: **Development**, **Preview**, **Production**. A variable set only for
Production is *absent* in preview — which is why preview builds fail with
"supabaseUrl is required" while production is fine.

Set per environment deliberately:
- Test/sandbox keys (Stripe `sk_test_`, Base Sepolia RPC) → Development + Preview
- Live keys → Production only

**`NEXT_PUBLIC_*` changes need a redeploy**, not just a save — they are baked in at
build time.

Vercel provides for free: `VERCEL_ENV`, `VERCEL_URL`, `VERCEL_GIT_COMMIT_SHA`,
`VERCEL_PROJECT_PRODUCTION_URL`. Use `VERCEL_ENV` to branch behaviour rather than
inventing your own flag.

```ts
const isProd = process.env.VERCEL_ENV === "production";
```

---

## Preview deploys

Every push gets a URL. The trap: **third-party allowlists do not know about it.**

Add the wildcard preview domain to:
- **Privy** dashboard → allowed domains (`*.vercel.app` or the project's preview pattern)
- **Alchemy** → allowed referrers
- **Supabase** → Auth → redirect URLs
- **Stripe** → webhook endpoint (or use the CLI locally instead)

A login that works in production and silently fails in preview is almost always this.

---

## Cron

```json
// vercel.json
{ "crons": [{ "path": "/api/cron/scan", "schedule": "*/15 * * * *" }] }
```

Cron routes are **publicly reachable URLs**. Protect them:

```ts
if (req.headers.get("authorization") !== `Bearer ${process.env.CRON_SECRET}`) {
  return new Response("no", { status: 401 });
}
```

Vercel sends that header automatically when `CRON_SECRET` is set. Limits: Hobby is
one cron/day; Pro allows minute-level. A 15-minute scanner needs Pro.

Functions have a **max duration** (10s Hobby, 60s+ Pro, configurable). A long scan
must either stream, chunk across invocations, or move to Railway — which is exactly
why our long-running P2P agent runs on Railway, not Vercel.

---

## Build failures — the usual four

1. **Type errors.** Vercel type-checks; `next dev` does not. Run `npx tsc --noEmit` before pushing.
2. **Missing env var** in that environment. `supabaseUrl is required` = the var is not set for Preview.
3. **Case-sensitive imports.** macOS is case-insensitive, the build container is not. `./Button` vs `./button` builds locally and fails on Vercel.
4. **Lockfile drift.** Commit the lockfile; the build uses it exactly.

---

## Gotchas

1. **Env var set for the wrong environment** — the #1 preview failure.
2. **`NEXT_PUBLIC_` without redeploy** stays stale.
3. **Preview URLs missing from third-party allowlists.**
4. **Unprotected cron routes** are public endpoints.
5. **Function timeout** on long jobs.
6. **Case-sensitive filesystem** in the build container.
7. **`.vercel/` is gitignored** — it holds the project link, and is per-developer.

