How to Move Environment Variables From Vercel to Railway (2026)

Railway's own migration guide gives the Vercel side of moving your environment variables in one line: "Export your variables (you can copy them directly)." For a lot of projects that's no longer true. Since August 24, 2026, every Vercel environment variable is either a Config value, which stays readable, or a Secret, which Vercel describes as write-only after saving. Your Vercel deployments still receive a Secret. You can't.

So the move has two halves. The readable values copy across in a minute. The Secrets have to be re-entered from wherever they came from, and the risk is a cutover where Railway quietly runs with a placeholder or a stale development key.

TL;DR

Run vercel env pull --environment=production into a new, empty file, paste the readable values into Railway's Raw Editor, and re-enter every Secret from its source (Stripe, Supabase, your email provider). Then diff the key names on both sides and swap any VERCEL_URL-style system variables for Railway's own. Pulling into an existing .env.local is the trap: for Secrets, the CLI keeps whatever value is already in the file.

Why the values don't all come with you

Vercel's Config/Secret split replaced the old Sensitive toggle. The changelog spells out the Secret side: the value "remains available to your deployments and can be replaced, but members cannot view or retrieve it after saving." Old Sensitive variables were converted to Secrets automatically.

Two defaults make this hit more variables than you'd guess:

  • In the CLI, vercel env add stores Production and Preview variables as Secret unless you pass --no-sensitive or --type config. Only Development defaults to Config.
  • In the dashboard, Secret is the type you pick for anything that looks like a key, which is exactly the set you most need on Railway.

If an AI agent or a teammate set up your Vercel project, you probably don't know which type each variable is. Assume the API keys are Secrets.

What vercel env pull actually writes

vercel env pull exports a project's variables to a .env-style file, and --environment=production picks the environment. For a Secret it can't read, Vercel's CLI docs say two different things happen depending on the file you point it at:

  • If the file has no real value for that key, the CLI writes a placeholder for you to replace.
  • If the file already has a value for that key, the CLI keeps it. Pull merges into an existing file rather than overwriting it.

The second case is the dangerous one. Run the pull against the .env.local you use for development and every production Secret in that file is silently your development value: the Stripe test key, the staging database URL. Paste that into Railway and the app boots, passes a smoke test, and charges nobody.

Always pull into a file that doesn't exist yet. A fresh file turns every unreadable Secret into an obvious placeholder you'll spot, instead of a plausible-looking wrong value you won't.

The move, step by step

1

Pull production into a new file. Use a name you won't confuse with your dev env, and keep it out of git.

Export what Vercel will give you
vercel env pull --environment=production .env.from-vercel
echo ".env.from-vercel" >> .gitignore
vercel env ls production    # every key name, for the checklist in step 3
2

Paste the readable values into Railway. Open your service, go to the Variables tab, and open the Raw Editor, which takes the contents of a .env or JSON file. Delete the placeholder lines before you save. Railway redeploys when variables change, so expect a build.

3

Re-enter each Secret from its source. Work down the key list from vercel env ls. For every value you couldn't read, go to where it was issued. This is the cheapest moment you'll ever get to rotate: issue a new key for Railway, and you can revoke the Vercel one cleanly after cutover instead of wondering which platform still uses it.

The Railway CLI can read a value from stdin, which keeps it out of your shell history:

Set a secret without typing it on the command line
pbpaste | railway variable set STRIPE_SECRET_KEY --stdin
4

Seal the secrets on Railway. Click the three-dot menu on each one and choose Seal. Railway then provides the value to builds and deployments but never shows it in the UI or returns it from the API, the same write-only deal Vercel's Secret type gives you. Do this last: Railway's docs note sealed variables can't be edited through the Raw Editor.

5

Diff the key names before you point DNS at Railway. Same set of names on both sides, or you have a gap.

Compare the two key lists
grep -v '^#' .env.from-vercel | cut -d= -f1 | sort -u > vercel-keys.txt
railway variable list --kv | cut -d= -f1 | sort -u > railway-keys.txt
diff vercel-keys.txt railway-keys.txt

Lines starting with < are keys you haven't moved yet. Lines starting with > exist only on Railway, which is fine for references like DATABASE_URL=${{Postgres.DATABASE_URL}}. Delete .env.from-vercel when you're done: it's a plaintext copy of your production config sitting on a laptop.

Variables Vercel set that Railway won't

Some of what your app reads was never in your variables list. Vercel injects system environment variables at build and runtime, and Railway doesn't set them:

Vercel setsRailway's nearest equivalent
VERCEL_URL (deployment domain, no https://)RAILWAY_PUBLIC_DOMAIN
VERCEL_ENV (production, preview, development)RAILWAY_ENVIRONMENT_NAME
VERCEL_GIT_COMMIT_SHARAILWAY_GIT_COMMIT_SHA

Search your code for VERCEL_ before cutover. A VERCEL_URL that's suddenly undefined usually shows up as auth callbacks or absolute links pointing at https://undefined. The values aren't identical either (your Railway environment is named whatever you named it, not necessarily production), so check any code that compares VERCEL_ENV === "production".

Variables created by a Vercel Marketplace integration, such as a Supabase or Neon connection string, also need a decision. Either paste the external connection string as a normal variable, or move the database too and use a Railway reference variable.

Prefixes still mean the same thing on Railway

Railway makes every service variable available during both the build and at runtime. Your framework still decides what reaches the browser: anything prefixed NEXT_PUBLIC_, VITE_ or NUXT_PUBLIC_ is inlined into the JavaScript bundle at build time, exactly as it was on Vercel.

That cuts both ways. Your public config keeps working without changes. And a secret that was wrongly prefixed on Vercel is just as public on Railway, sealed or not. A migration is a good time to look for one: if any of the Secrets you just re-entered carry a public prefix, fix the name, not the platform.

How do I move environment variables from Vercel to Railway?

Pull the production values into a fresh file with vercel env pull --environment=production, paste the readable ones into the Raw Editor on your Railway service's Variables tab, then re-enter every Secret by hand from its original source, because Vercel won't return Secret values. Finish by diffing the key names on both sides.

Why does vercel env pull leave some values blank or wrong?

Those keys are Secrets. Since August 2026 every Vercel variable is either Config, which stays readable, or Secret, which is write-only after saving. The CLI can't read a Secret back, so it writes a placeholder, or, if the target file already has a value for that key, keeps whatever was there.

Can I export Vercel Secret environment variables at all?

No. Vercel's docs describe Secret values as write-only after saving: your deployments still get them, but no team member can view or retrieve them. Get each one from where it was issued (Stripe, Supabase, OpenAI and so on), and treat the move as a good moment to issue a fresh key for Railway.

What replaces VERCEL_URL and VERCEL_ENV on Railway?

Nothing sets them for you. Railway provides its own variables instead: RAILWAY_PUBLIC_DOMAIN for the service's domain, RAILWAY_ENVIRONMENT_NAME for the environment, and RAILWAY_GIT_COMMIT_SHA for the commit. Any code that reads a VERCEL_ variable needs to be pointed at the Railway one or given an explicit value.

Just moved to Railway?

We scan your live site for API keys that landed in the JavaScript bundle, whichever platform built it.

How-To Guides

How to Move Environment Variables From Vercel to Railway (2026)