Migrate from Vercel
The workflow you have on Vercel — connect a repo, push, get a live URL, preview deploys per PR — is the workflow slew is built around. A migration is four things: connect the git repo, copy the env vars, translate the config, point DNS. The importer does the first three in one command, and nothing on Vercel changes until you flip DNS yourself, so there is no moment where the site is down and no way back.
The short version
cd your-project
slew login
slew import vercel --dry-run # compat report + plan, creates nothing
slew import vercel # do it
The importer reads vercel.json and package.json, and — when a Vercel
token is set (--token or VERCEL_TOKEN, in a directory linked with
vercel link) — also pulls the project's build settings, production env
vars, connected repo and domains from the Vercel API. It is strictly
read-only towards Vercel. It then:
- Prints a compatibility report. Hard incompatibilities stop the import before anything is created; everything else is stated plainly and carried on.
- Creates the slew project and links the directory.
- Copies production env vars (scope
both, so builds see them too). Sensitive vars — which Vercel's API never returns decrypted — are listed by name to set by hand. - Recreates
cronsand translatesredirectsinto slew's redirect rules. - Connects the GitHub repo with your Vercel build settings — every push now builds and deploys, PRs get preview URLs.
- Prints the DNS cutover checklist below, filled in with your actual domains.
No Vercel token? The importer still does everything it can from the local files, and env vars are two commands:
vercel env pull --environment=production .env.production
slew env import .env.production --scope both
slew env import reads any dotenv file and skips the VERCEL_* keys
Vercel injects.
What maps to what
| On Vercel | On slew |
|---|---|
| git push → deploy | Same — git push-to-deploy via the GitHub App |
| Preview deployments per PR | Same — expiring preview URLs, commented on the PR |
| Preview branch domain | Branch domains: slew domains add staging.example.com --branch staging |
| Environment variables | slew env — encrypted at rest, runtime/build/both scopes |
vercel.json crons |
slew cron — 5-field cron, UTC, up to 5 per project |
vercel.json redirects |
slew.json redirects — translated by the importer |
ISR / revalidate |
Works — platform cache + on-demand revalidation endpoint |
| Image optimization | Included on every plan, served from the CDN edge |
| Analytics | Included, cookieless, every plan |
| Instant rollback | slew rollback <deployment-id> — re-points, no rebuild |
| Serverless / edge functions | Different model: your app runs as one real Node server — see below |
What's genuinely different
Being straight about this is the point — a migration that fails on first deploy is worse than one that tells you up front:
- One runtime: Node 22, as a long-running server. No Python, Go, Ruby
or Rust function runtimes, no edge runtime. The importer refuses to
continue if
vercel.jsondeclares one. - No serverless decomposition. Your Next.js app runs in standalone
mode as the server the framework was written to be. That kills
cold-start arithmetic and makes WebSockets and in-process state work —
and it means per-function config (
functions,regions) has nothing to configure. - Redirects and same-site rewrites translate; headers don't.
vercel.jsonredirects carry over with their exact status codes, and rewrites whose destination is a path on your own site become slew rewrites. Rewrites that proxy to another origin, andheaders, are the exceptions: for Next.js they cost nothing —rewrites()andheaders()innext.config.jswork on slew because the app is a real server — but static sites have no platform-level headers yet. - One region. Rendering happens in Germany behind a CDN; static assets and ISR pages are cached at the edge. If you need rendering in dozens of PoPs, slew is the wrong tool — better to read that here than find out in production.
middlewareruns in your server, in the EU, not on an edge fleet. Same behavior, one region.cleanUrlsis the default — extensionless paths already resolve to their.htmlfile. Just drop the key.
Per-framework notes
Next.js — add one line, if it isn't there already:
// next.config.js
module.exports = { output: 'standalone' }
The builder detects the standalone build and deploys it as a server; SSR,
API routes, middleware, server actions, ISR and next/image all work.
Details on the Next.js page.
Astro — static builds deploy as-is; SSR needs @astrojs/node
(recipe). SvelteKit — swap @sveltejs/adapter-vercel
for @sveltejs/adapter-node (SSR) or @sveltejs/adapter-static
(recipe). Remix — works via the
Remix recipe. Static sites (Vite, Astro, plain HTML) —
nothing to change.
Lockfiles: the builder installs server-app production dependencies
with npm ci, which needs a committed package-lock.json. On pnpm, yarn
or bun, generate one alongside: npm i --package-lock-only. The importer
warns when this applies to you.
Cutting over DNS
The one step the importer can't do for you. Nothing about your Vercel setup has changed yet — the site keeps serving from Vercel until DNS moves, and moving it back is the same edit in reverse.
- Check the deploy at
https://<project>.slew.cloud.slew git logprints the build log; click through the site, verify env-dependent pages work. - Lower the TTL on your domain's DNS record to 300 seconds, and wait out the old TTL. This makes the actual switch near-instant — and makes a rollback near-instant too.
slew domains add example.com— prints the CNAME target to point the record at (apex domains: use your provider's ALIAS/ANAME, or the A record the command prints).- Flip the record, then run
slew domains add example.comagain: TLS is provisioned once DNS resolves to the CDN, usually within a minute.slew domains cert example.comconfirms the certificate. Expect a brief window on the very first switch where early resolvers reach slew before the certificate is issued — the 300s TTL keeps that window to about a minute; re-adding the domain retries issuance immediately. - Remove the domain from the Vercel project, so Vercel's edge stops answering for it, and put the TTL back up.
Repeat 3–5 per domain. A www + apex pair counts as two records.
After the switch
- Watch it:
slew logs -f,slew stats, and the console's Traffic tab. - Preview deploys, branch domains and crons are already running — nothing else to enable.
- Keep the Vercel project around, paused, for a week if it makes you comfortable. Rollback is: point DNS back. When you're sure, delete it.
Coming from Netlify instead? Same idea, own page: Migrate from Netlify.
Stuck anywhere? Email support — a first migration is exactly the kind of thing worth doing together over a screen share.