Railway Environment Variables: Complete Setup Guide (2026)

How-To Guide

Railway Environment Variables: Complete Setup Guide (2026)

Manage secrets and configuration in Railway

Railway gives you four places to put a variable: per-service, project-wide Shared, ephemeral PR environments, and built-in RAILWAY_* values that Railway sets for you. Pick the wrong one and you'll either leak a key into a public PR preview or paste the same Stripe secret into six services and forget one when you rotate it.

This guide walks the dashboard UI step-by-step, then covers the four sources, the ${{...}} reference syntax, the CLI, and the gotchas that bite vibe-coded apps deploying their first paid service.

TL;DR

TL;DR: In Railway, click your service, open the Variables tab, and add key-value pairs. Railway auto-redeploys on save. Reference values across services with ${{SERVICE.VAR}}. Use Shared Variables for keys multiple services need, and Sealed Variables for production secrets you don't want anyone reading back from the dashboard.

Step-by-Step Setup

1

Open your project

Go to railway.app/dashboard and click on your project.

2

Select your service

Click on the service you want to configure (e.g., your web app, API, or database).

3

Click the Variables tab in the service panel.

4

Add variables

You have two options:

  • Add individually: Click "New Variable" and enter key/value
  • Raw Editor: Click "RAW Editor" to paste multiple variables at once
# In Raw Editor, paste like a .env file:
DATABASE_URL=postgresql://...
STRIPE_SECRET_KEY=sk_test_xxxxx
OPENAI_API_KEY=sk-xxxxx
5

Save and deploy

Railway automatically triggers a redeploy when you save variables. You'll see the deployment start in the Deployments tab.

Railway-Specific Features

Variable References

Railway lets you reference variables from other services using the ${{SERVICE.VAR}} syntax:

# Reference the database URL from your Postgres service
DATABASE_URL=${{Postgres.DATABASE_URL}}

# Reference another service's variable
API_URL=${{api.RAILWAY_PUBLIC_DOMAIN}}

Built-in Railway Variables

Railway injects these automatically into every service. You do not need to define them:

# Available in every service (no setup required)
RAILWAY_ENVIRONMENT=production
RAILWAY_PUBLIC_DOMAIN=your-app.up.railway.app
RAILWAY_PRIVATE_DOMAIN=your-app.railway.internal
RAILWAY_PROJECT_ID=xxx
RAILWAY_SERVICE_ID=xxx

RAILWAY_PUBLIC_DOMAIN is the most useful one for app code. Use it to build absolute URLs for webhooks, OAuth callbacks, and link generation instead of hardcoding your domain:

# In your service's variable panel, reference the built-in to build a full URL
WEBHOOK_URL=https://${{RAILWAY_PUBLIC_DOMAIN}}/webhooks/stripe
OAUTH_CALLBACK=https://${{RAILWAY_PUBLIC_DOMAIN}}/auth/callback

Built-in variables are read-only. You cannot override RAILWAY_PUBLIC_DOMAIN from the dashboard. If you need a custom domain, set a separate variable like APP_BASE_URL=https://yourdomain.com and use that in your code instead.

Postgres DATABASE_URL

When you add a Railway Postgres service to your project, Railway automatically injects DATABASE_URL and DATABASE_PUBLIC_URL into that database service. To use them in your app service, reference them explicitly:

# In your app service's Variables tab:
DATABASE_URL=${{Postgres.DATABASE_URL}}

If you don't add the reference, your app won't see the variable. Railway doesn't auto-link database variables to other services. The ${{Postgres.DATABASE_URL}} reference is the manual wire you need to add.

Shared Variables

For variables needed across multiple services:

  1. Click "Shared Variables" at the project level
  2. Add your variable (e.g., STRIPE_SECRET_KEY)
  3. In each service, reference it: STRIPE_SECRET_KEY=${{shared.STRIPE_SECRET_KEY}}

Pro tip: Use Shared Variables for API keys that multiple services need. You update the key in one place when rotating, and every service that references it picks up the new value on its next deploy.

Sealed Variables

Sealed Variables hide a value after you save it. The dashboard shows •••••• instead of the secret, builds and runtime still get the real value, and your teammates can't accidentally screenshot a Stripe live key into a Slack thread.

Use Sealed for: production database passwords, Stripe live keys, OAuth client secrets, third-party API keys with billing attached.

Sealed values can't be read back from the UI or CLI once saved. If you forget the value, you rotate the upstream secret and re-enter it.

PR (Pull Request) Environments

Railway can spin up an ephemeral environment for every open PR, isolated from production. The variables work in three layers:

  1. Inherited: Variables from the base environment (usually production or staging) carry into the PR env.
  2. Overridden: Set a variable in the PR environment to override the inherited value (e.g., a staging Stripe test key, a separate Postgres database URL).
  3. Built-in PR vars: Railway exposes RAILWAY_GIT_BRANCH, RAILWAY_GIT_COMMIT_SHA, and RAILWAY_GIT_AUTHOR so the preview can show a "Preview for branch X" banner.

Don't inherit production secrets into PR environments. A PR env is reachable on a public URL by default. If you inherit STRIPE_SECRET_KEY=sk_live_..., anyone with the preview URL can hit your live Stripe account. Override with the test key in the PR environment, or scope the secret to production-only.

Using the Railway CLI

# Link to your project
railway link

# List variables for current service
railway variables

# Add a variable
railway variables set STRIPE_SECRET_KEY=sk_test_xxxxx

# Get a specific variable
railway variables get DATABASE_URL

# Delete a variable
railway variables delete OLD_KEY

# Run local dev with Railway variables
railway run npm run dev

Common Variable Patterns

# Database (auto-populated when adding Railway Postgres)
DATABASE_URL=${{Postgres.DATABASE_URL}}

# Redis (auto-populated when adding Railway Redis)
REDIS_URL=${{Redis.REDIS_URL}}

# Internal service communication
API_INTERNAL_URL=http://${{api.RAILWAY_PRIVATE_DOMAIN}}:3000

# External URL for webhooks
WEBHOOK_URL=https://${{RAILWAY_PUBLIC_DOMAIN}}/api/webhook

# Secrets (use Shared Variables)
STRIPE_SECRET_KEY=${{shared.STRIPE_SECRET_KEY}}
OPENAI_API_KEY=${{shared.OPENAI_API_KEY}}

Private vs Public Domains: RAILWAY_PRIVATE_DOMAIN is for internal service-to-service communication (faster, no public internet). RAILWAY_PUBLIC_DOMAIN is your externally reachable URL. Use private domains when one service inside Railway talks to another, since traffic stays on the internal network and skips egress charges.

How do I add environment variables in Railway?

Open your project at railway.app/dashboard, click the service you want to configure, open the Variables tab, and click New Variable to enter a key and value. Railway redeploys the service automatically when you save. To paste many at once, use Raw Editor and drop in .env-formatted lines.

What's the difference between Railway shared and service variables?

Service variables live on a single service and are scoped to that service only. Shared Variables sit at the project level and are referenced from each service via ${{shared.NAME}}. Use shared for values multiple services need (like a Stripe secret) so you only rotate them in one place. Use service variables for things only one service should ever see.

How do secrets work in Railway?

Every variable in Railway is encrypted at rest and only injected into builds and runtime processes you authorize. Mark variables as Sealed to hide their value from the dashboard after initial entry (you can still reference them, but you can't read them back). Never put a secret in a build-time NEXT_PUBLIC_ or VITE_ variable, since those get baked into the client bundle.

Do I need to restart after changing variables?

No. Railway automatically triggers a new deployment when you save a variable change. The new deployment runs with the updated values; the old deployment stays live until the new one passes health checks.

How do I use Railway variables locally?

Run railway link once to connect your local directory, then prefix any command with railway run to inject the linked service's variables. Example: railway run npm run dev. This avoids copying secrets into a local .env file.

What is RAILWAY_PUBLIC_DOMAIN and how do I use it?

RAILWAY_PUBLIC_DOMAIN is a built-in variable Railway injects automatically. It holds your service's public URL (e.g., your-app.up.railway.app). Reference it in other variables to build full URLs without hardcoding: WEBHOOK_URL=https://${{RAILWAY_PUBLIC_DOMAIN}}/webhooks/stripe. You cannot override it from the dashboard. If you use a custom domain, create a separate variable like APP_BASE_URL=https://yourdomain.com and reference that instead.

Find leaked secrets, NEXT_PUBLIC_ slips, and exposed admin paths in 2 minutes. Free.

How-To Guides

Railway Environment Variables: Complete Setup Guide (2026)