---
title: "How to deploy to Vercel"
slug: "deploy-to-vercel"
category: "howto"
tags: ["howto", "vercel", "deploy", "hosting", "tutorial", "frontend"]
status: "stable"
last_updated: 2026-05-14
summary: "Import a project, set environment variables, configure the build, attach a custom domain, and enable preview deploys on every PR."
related: ["[[ops/vercel]]", "[[frontend/nextjs]]", "[[frontend/astro]]", "[[ops/cloudflare]]", "[[seo/technical]]", "[[howto/deploy-quartz-site]]", "[[seo/core-web-vitals]]"]
---

## Overview

Vercel is the fastest zero-config host for Next.js, Astro, and most other Node-based frameworks. This guide covers the full lifecycle: project import, environment variables, build override, custom domain, and preview deploys. The deep-dive on Vercel-specific optimizations (Edge Middleware, ISR, split testing) lives in [[ops/vercel]].

## Prerequisites

- A GitHub, GitLab, or Bitbucket account with the repository you want to deploy.
- A Vercel account. The Hobby plan covers personal projects; Pro is required for team features and custom preview domain suffixes.
- The `vercel` CLI installed locally (optional but useful for debugging): `npm i -g vercel`.

## Steps

### 1. Import the project

1. Log in at [vercel.com](https://vercel.com) and click **Add New > Project**.
2. Connect the Git provider if you have not already. Authorize Vercel to read repositories.
3. Find your repository in the list and click **Import**.
4. Vercel auto-detects the framework (Next.js, Astro, Vite, etc.) and pre-fills build settings. Verify them before continuing.

### 2. Set environment variables

Click **Environment Variables** before the first deploy. Add each variable Vercel needs at build time or runtime.

```
DATABASE_URL        = postgresql://...
NEXT_PUBLIC_API_URL = https://api.example.com
SECRET_KEY          = <generate with openssl rand -hex 32>
```

Mark variables `Preview` or `Production` only when their values differ between environments. Variables prefixed `NEXT_PUBLIC_` are inlined into the client bundle; never put secrets in them. See [[ops/vercel]] for the encrypted secrets workflow.

### 3. Override build settings if needed

For most frameworks the defaults are correct. Override when:

- The build command is non-standard, e.g., `npm run build:prod`.
- The output directory differs from the framework default (`out/`, `dist/`, `.next/`).
- The install command must be `npm ci` instead of `npm install`.

Set these under **Settings > Build & Development Settings** after the first deploy, or in `vercel.json` at the repo root:

```json
{
  "buildCommand": "npm run build:prod",
  "outputDirectory": "dist",
  "installCommand": "npm ci"
}
```

### 4. Deploy

Click **Deploy**. Vercel clones the repo, installs dependencies, runs the build command, and uploads the output. The first deploy takes 1 to 3 minutes. Watch the build log for errors; they appear in real time.

### 5. Attach a custom domain

1. Go to **Settings > Domains** on the project page.
2. Type your domain, e.g., `example.com`, and click **Add**.
3. Vercel shows a DNS record. Add it at your registrar or at [[ops/cloudflare]] if DNS is proxied through Cloudflare.
   - If Cloudflare proxies the domain, use a `CNAME` to `cname.vercel-dns.com` with the Cloudflare proxy **off** (grey cloud), or configure a Cloudflare Worker to forward requests. Vercel's TLS provisioning fails through an orange-cloud proxy.
4. Vercel provisions a Let's Encrypt certificate automatically once the DNS record resolves.

### 6. Enable preview deploys

Preview deploys are on by default. Every push to a non-production branch gets its own URL at `<branch>--<project>.vercel.app`. Share it with reviewers before merging.

Configure branch-specific environment variable overrides under **Settings > Environment Variables** by selecting the target branches. This lets staging use a staging database without a separate project.

## Verify it worked

```bash
# 1. Production URL returns 200.
curl -sI https://example.com | head -3
# HTTP/2 200

# 2. SSL certificate is valid and issued by Let's Encrypt.
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -issuer

# 3. A push to a feature branch creates a preview URL.
git checkout -b test/preview-deploy
git commit --allow-empty -m "test: trigger preview deploy"
git push origin test/preview-deploy
# Vercel bot posts a comment to the PR with the preview URL.
```

## Common errors

- **Build fails with "Command not found"**. The framework CLI is not in `devDependencies`. Add it to `package.json` and redeploy.
- **Environment variable is undefined at runtime**. The variable was set as `Preview`-only but the deploy is `Production`, or vice versa. Check the scope in **Settings > Environment Variables**.
- **Custom domain shows "Invalid Configuration"**. Cloudflare orange-cloud proxy is interfering with Vercel's TLS challenge. Set the Cloudflare proxy to DNS-only for the `www` record until the certificate provisions, then re-enable the proxy.
- **404 on all routes except `/`**. The framework uses client-side routing but the output directory contains only an `index.html`. Add a `vercel.json` with `"rewrites": [{"source": "/(.*)", "destination": "/index.html"}]`.
- **Old code appears after deploy**. Vercel cached a CDN edge node. Trigger a redeploy from the Vercel dashboard, or run `vercel --force` from the CLI.

## Related

- [[ops/vercel]]
- [[frontend/nextjs]]
- [[frontend/astro]]
- [[ops/cloudflare]]
- [[seo/technical]]
- [[howto/deploy-quartz-site]]
- [[seo/core-web-vitals]]
