Moving from Vercel
Move a Vercel project to Deplane with one command. deplane import vercel reads the project on Vercel and shows what moves and what stays. It then creates the Deplane app with its production variables and a cron job for each Vercel cron, and deploys your code. Vercel keeps serving your users until you switch.
The command runs on your own computer with your Vercel token, which never leaves it. It only reads Vercel: nothing there is changed. The command and its flags are in the CLI reference, and every error has a fix in Troubleshooting and error codes. It works like Moving from Heroku.
Before you start
- Install the Deplane CLI and run
deplane login. - Have the project's code in a folder: a checkout of the repository Vercel deploys, at the commit it runs. If Vercel builds a subfolder (its Root Directory), pass that folder with
--source. - Create a Vercel token: Account Settings → Tokens → Create, scoped to the project's team, with a short expiration.
The Vercel token. A Vercel token can do everything your account can in its scope; there are no read-only tokens. The CLI only reads with it (GET requests to api.vercel.com). Delete it when the import is done.
Give the token to Deplane in one of three ways, never as a flag:
- run
deplane import vercel my-team/my-app --checkin a terminal and paste it at the hidden prompt; - set it for one command:
VERCEL_TOKEN=... deplane import vercel my-team/my-app --check(it lands in your shell history; the prompt does not); - or pipe it in from a password manager:
op read op://Private/vercel/token | deplane import vercel my-team/my-app --check.
A token piped on stdin wins over VERCEL_TOKEN. The CLI says which one it used, never the token itself. It is never written to disk, never printed, left out of --debug, and never sent to Deplane.
The team. A project that belongs to a team needs the team: put its slug or ID first (my-team/my-app), or set VERCEL_TEAM_ID. Without either, the CLI uses the team in the folder's .vercel/project.json (vercel link writes it), or your personal account.
Secret variables
Vercel never gives back the values of Secret variables (called sensitive before), to anyone: not in the dashboard, the API or vercel env pull. The import lists them by name, copies the others, and creates the app without deploying it, since it would start without them. The next steps show what to set before you deploy:
deplane secrets set STRIPE_SECRET_KEY=... --app my-app
deplane secrets set --build STRIPE_SECRET_KEY=... --app my-app # if the build reads it too
cd my-app && deplane launch --app my-app # deploys the app and writes deplane.toml
Take the values from where you keep them: a password manager, or the service that issued them.
Check first
$ deplane import vercel acme/shop --check
Vercel project shop (framework nextjs, team acme, repo acme/shop@main, commit 1a2b3c4) → Deplane app shop
Code
✓ Deploys /home/me/shop with Railpack: Detected Next.js 15, Node 22, port 3000.
✓ Your folder is at commit 1a2b3c4, the one Vercel runs.
✓ Vercel builds this project as Next.js; Railpack builds it too, and Deplane runs it with `next start`.
⚠ Next.js on `next start` keeps middleware, API routes, ISR and image optimization ...
Processes
✓ web: Vercel runs the project as functions on demand → 1 small instance. Set more with `deplane scale`.
✓ cron `0 5 * * *` /api/cron/sync on Vercel → cron job cron-sync, `0 5 * * *` (UTC), on a small instance: ...
⚠ Vercel calls a path of the app on each schedule; the Deplane cron jobs make the same request to http://shop/PATH ...
Data
✓ Copied as they are, so the app keeps using those databases where they are: DATABASE_URL (ep-cool-name.eu-central-1.aws.neon.tech). ...
Config vars
✓ 3 copied as secrets: CRON_SECRET, DATABASE_URL, NEXT_PUBLIC_SITE_URL.
✓ 3 also set as build secrets, since Vercel gives every variable to the build too ...
⚠ Not copied, Vercel never gives back the values of Secret (sensitive) variables: STRIPE_SECRET_KEY. ...
Domains
⚠ www.shop.example points at Vercel (cname.vercel-dns.com). It moves when you switch ...
Plan
✓ Fits your Team plan: after the import the team uses 2 of 40 instances and 1 GB of 16 GB memory.
Nothing was changed (--check). Run the command again without --check to import.
✓ is what moves as it is, ⚠ is what you should know or do yourself, and ✗ is what stops the import. --check --json prints the same report as one JSON document. Then run the command again without --check (in a terminal it asks first; from a script, add --yes).
What moves
| On Vercel | On Deplane |
|---|---|
| A Next.js project | An app that Railpack builds and runs with next start: pages, API routes, middleware, ISR and image optimization work there. |
| A static site (Vite, Create React App, Astro, Gatsby ...) | An app that Railpack builds and serves as files. |
| SvelteKit, Nuxt, Remix, SolidStart, server-rendered Astro | An app, once it builds a Node server: switch from the Vercel adapter to the framework's Node adapter (the report says which). |
| Production environment variables (Config) | Secrets, and build secrets too, since Vercel gives every variable to the build (NEXT_PUBLIC_ ones are built into the pages). Not copied: VERCEL_* and system variables, and variables set only for Preview, Development or a branch. |
| Secret (sensitive) variables | Listed by name, not copied: see Secret variables. |
Cron jobs (crons in vercel.json) | Cron jobs with the same schedule (UTC on both). Each makes the request Vercel makes, GET on the path, to http://APP/PATH over the private network, with CRON_SECRET as the bearer token only when it is set (with node, or curl for an app without Node). Day and month names are converted; a schedule Deplane cannot run is a ✗ in the report. A cron job the app cannot answer is listed with the command that adds it later, and not created: on a plan where apps sleep (Free), for a path that is a Vercel Function outside Next.js, and for a site that serves only files. Cron jobs turned off on Vercel are not created. |
| Production domains | Listed, and moved at the switch. *.vercel.app stays with Vercel. |
| Databases and stores you connected (Neon, Supabase, Upstash ...) | Their URLs are copied as they are, and the app keeps using them. To move a Postgres database later, use deplane db import. |
| The code | Your folder, built by Railpack, or by your Dockerfile if it has one. |
Instances: Vercel has none to map. The app starts with one small instance (0.5 CPU, 512 MB); a Next.js app often needs medium: deplane scale --size medium.
What does not move
- Vercel's own services: its Edge Network cache, Edge Config, Blob, KV, Analytics, Speed Insights, Firewall and image CDN. The app keeps using Blob and Edge Config with the copied variables while they exist on Vercel.
- ISR's cache is kept by each instance on Deplane, not shared between instances. Edge-runtime routes run on Node.
- Vercel Functions outside Next.js (an
api/folder next to a static site) and Routing Middleware do not run: serve those routes from your app's own server. vercel.jsonrewrites, redirects, headers,cleanUrlsandtrailingSlashare applied by Vercel, not by Deplane: move them intonext.config.jsor your server.- Build overrides (build command, output directory, install command, Node version): Railpack detects the build itself; set the Node version in
package.json'sengines. - Cron jobs on a sleeping app: an app that sleeps when idle does not wake for a request over the private network. On Free, apps always sleep, so the import does not create the cron jobs there: the report lists each with the command to add it after moving to Hobby or a larger plan and turning sleep off (
deplane scale --sleep off). - Redirect domains are listed: add them to the app and redirect in your code.
- Deplane runs in one region, in Europe.
Switch over
- Set the Secret variables and deploy, if the import listed any. Test the app at its Deplane address.
- Before the day: run
deplane domains add HOSTNAME --app APPfor each custom domain and add the TXT record it shows. - Disable Vercel's cron jobs (the project's Settings → Cron Jobs → Disable), so they do not run on both. The Deplane cron jobs run from the first deploy, and a cron job cannot be paused: if they must not run twice before the switch, disable Vercel's earlier, or remove Deplane's (
deplane processes remove NAME) and add them again now. - Switch DNS to the target
deplane domains addshowed, and wait for the certificate (deplane domains check HOSTNAME --app APP). - Remove the domains from the Vercel project, and delete it once the app has run well on Deplane.
- Delete the Vercel token in Account Settings → Tokens.
Limits
The Heroku import's limits apply: 10 workers and cron jobs per app, sizes up to 2 GB. Secret variables are not copied. The import is built on Vercel's public REST API; tell support@deplane.io if Vercel answers something the CLI does not expect.
This page is also available as Markdown. AI agents can start at /llms.txt.