# Deploy SaaS kit to Railway

> Learn how to deploy your SaaS application to Railway.

*Canonical: https://makerkit.dev/docs/nextjs-prisma/going-to-production/railway*

---

Railway is an all-in-one deployment platform that provides hosting, PostgreSQL database, and S3-compatible storage in a single dashboard. Deploy your MakerKit application by connecting your GitHub repo, adding a Postgres database and storage bucket, configuring environment variables using Railway's reference syntax (`${{Postgres.DATABASE_URL}}`), and running migrations. Railway handles SSL, auto-deploys on push, and provides built-in monitoring.

This guide walks through the complete deployment process step by step.

{% alert type="default" title="Flexible Hosting Options" %}
While this guide uses Railway's integrated Postgres and S3-compatible storage for simplicity, you can use external providers for your database (Neon, PlanetScale, Supabase, AWS RDS) and storage (AWS S3, Cloudflare R2, DigitalOcean Spaces). Railway makes it easy to connect to external services via environment variables. However, Railway's all-in-one approach provides an excellent developer experience with minimal configuration.
{% /alert %}

## Prerequisites

Before you begin, ensure you have:

1. A [Railway account](https://railway.app)
2. Your application code pushed to a GitHub repository
3. Your billing provider credentials (Stripe or Polar)

## Architecture Overview

Your Railway deployment will consist of three services:

1. **Web Service** - Your Next.js application
2. **Postgres Database** - Railway's managed PostgreSQL instance (if you use a different database, you can use the external provider's database)
3. **Storage Bucket** - S3-compatible object storage for file uploads (if you use a different storage provider, you can use the external provider's storage)

## Step 1: Create a New Railway Project

1. Log in to your [Railway dashboard](https://railway.app/dashboard)
2. Click **New Project**
3. Select **Deploy from GitHub repo**
4. Connect your GitHub account if not already connected
5. Select your MakerKit repository

Railway will automatically detect your Next.js application and begin the initial deployment setup.

**Note**: the initial deployment will fail because we need to configure the environment variables. It's okay, we will do that in the next steps.

## Step 2: Add a PostgreSQL Database

1. In your project canvas, click the **+ New** button
2. Select **Database** → **Add PostgreSQL**
3. Railway will provision a PostgreSQL instance with SSL enabled

The database will appear in your architecture view with a `postgres-volume` for persistent storage.

We will need the variable `DATABASE_URL` to connect to the database. You can find it in the **Credentials** tab of the Postgres service.

## Step 3: Add S3-Compatible Storage

Railway provides S3-compatible storage buckets for file uploads:

1. Click the **+ New** button in your project canvas
2. Select **Storage Bucket**
3. Choose your preferred region (e.g., US West - California, USA)

{% alert type="warning" title="Region Selection" %}
The bucket's region cannot be changed after creation. Choose a region close to your web service for optimal performance.
{% /alert %}

After creation, click on the Storage Bucket service to view:
- **Credentials tab**: Contains your S3 access credentials
- **Settings tab**: Shows the bucket region and management options

## Step 4: Configure Environment Variables

Click on your **web** service and navigate to the **Variables** tab. You need to configure the following environment variables:

### Database Configuration

```bash
DATABASE_URL=${{Postgres.DATABASE_URL}}
```

Railway automatically injects the `DATABASE_URL` when you reference it using the `${{Postgres.DATABASE_URL}}` syntax. This ensures your web service always has the correct connection string.

### Site Configuration

Please copy the exposes URL of your web service that you find in the `web` service and set it as the `NEXT_PUBLIC_SITE_URL` environment variable.

```bash
NEXT_PUBLIC_SITE_URL=https://your-domain.up.railway.app
```

Replace with your actual Railway domain or custom domain.

### Storage Configuration

Configure the S3-compatible storage variables from your Storage Bucket credentials:

```bash
STORAGE_BASE_URL=https://your-bucket.railway.storage
STORAGE_S3_ACCESS_KEY_ID=your-access-key
STORAGE_S3_SECRET_ACCESS_KEY=your-secret-key
STORAGE_S3_BUCKET=your-bucket-name
STORAGE_S3_REGION=us-west-1
```

{% alert type="default" title="Storage Credentials" %}
You can find these credentials in the Storage Bucket service under the **Credentials** tab.
{% /alert %}

### Billing Provider Configuration

Set your billing provider and its credentials:

```bash
NEXT_PUBLIC_BILLING_PROVIDER=stripe
```

The `NEXT_PUBLIC_BILLING_PROVIDER` can be one of: `stripe` or `polar`.

#### For Stripe:

```bash
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
```

#### For Polar:

```bash
NEXT_PUBLIC_BILLING_PROVIDER=polar
POLAR_ACCESS_TOKEN=your-polar-access-token
```

### Complete Environment Variables List

Here's a summary of all the minimum required environment variables that you need to set:

| Variable | Description |
|----------|-------------|
| `DATABASE_URL` | PostgreSQL connection string (use Railway reference) |
| `NEXT_PUBLIC_SITE_URL` | Your production URL |
| `BETTER_AUTH_SECRET` | Random secret for auth tokens (use `openssl rand -base64 32`) |
| `NEXT_PUBLIC_BILLING_PROVIDER` | `stripe` or `polar` |
| `STORAGE_BASE_URL` | S3 storage endpoint URL |
| `STORAGE_S3_ACCESS_KEY_ID` | S3 access key |
| `STORAGE_S3_SECRET_ACCESS_KEY` | S3 secret key |
| `STORAGE_S3_BUCKET` | S3 bucket name |
| `STORAGE_S3_REGION` | S3 bucket region |

Plus your billing provider-specific variables as listed above.

**Note**: please make sure to add the environment variables that you retrieved from the [previous steps](./environment-setup) to your Railway project so that you can successfully customize your deployment when you go live, otherwise the default values will be used (e.g. the ones in `.env` and `.env.production`). If you modified these values directly, it's okay as well.

## Step 5: Run Database Migrations

After setting up your environment variables, run Prisma migrations to create your database schema.

Copy the `DATABASE_URL` from Railway's Postgres **Variables** tab and run:

```bash
read -s DATABASE_URL && export DATABASE_URL && npx prisma migrate deploy --schema packages/database/src/prisma/schema.prisma
```

This command:
1. Prompts you to paste the URL without displaying it in your terminal history
2. Runs `prisma migrate deploy` to apply existing migrations

{% alert type="warning" title="migrate deploy vs migrate dev" %}
Use `prisma migrate deploy` for production. It applies existing migrations without creating new ones. The `prisma migrate dev` command is for development only.
{% /alert %}

## Deployment Workflow

Once configured, Railway automatically deploys your application when you push to your connected branch:

1. Push changes to GitHub
2. Railway detects the push and starts a new deployment
3. The build runs with your configured settings
4. If successful, the new version goes live

## Monitoring and Logs

Railway provides built-in monitoring:

- **Deployments tab**: View deployment history and status
- **Logs tab**: Real-time application logs
- **Metrics tab**: CPU, memory, and network usage
- **Observability**: Application performance monitoring

## Troubleshooting

### Database Connection Issues

If your application can't connect to the database:

1. Verify `DATABASE_URL` is correctly set using Railway's variable reference syntax
2. Check that your Postgres service is **Online**
3. Ensure you're using the private networking URL for internal connections

### Build Failures

If your build fails:

1. Check the build logs in the **Deployments** tab
2. Verify all required environment variables are set
3. Ensure your `package.json` scripts are correct
4. For monorepos, verify the root directory setting

### Storage Issues

If file uploads aren't working:

1. Verify all `STORAGE_S3_*` variables are correctly set
2. Check that the Storage Bucket service is active
3. Ensure your application code uses the correct S3 client configuration

## Pricing Considerations

Railway offers usage-based pricing. Key points for MakerKit deployments:

- **Hobby plan ($5/month)**: Includes $5 of usage credits, suitable for development and low-traffic production apps. Apps may sleep after inactivity.
- **Pro plan ($20/month)**: Includes $20 of usage credits, no sleep behavior, team features, and priority support.
- **Usage costs**: Billed per GB of RAM, vCPU hours, storage, and egress. A typical MakerKit app with light traffic costs $5-15/month in usage.

For current pricing, see [Railway's pricing page](https://railway.app/pricing).

## Common Pitfalls

Watch out for these Railway-specific issues:

- **Private vs public DATABASE_URL**: Use the private networking URL (starts with `${{Postgres...}}`) for internal connections. The public URL adds latency and egress costs.
- **Storage bucket region is permanent**: Choose the region carefully during creation—it cannot be changed afterward.
- **Build minutes count toward usage**: Large monorepo builds consume credits. Optimize your Dockerfile or use build caching.
- **Hobby plan sleep behavior**: Applications on the Hobby plan may sleep after 10 minutes of inactivity. Upgrade to Pro for always-on deployments.

## Production Checklist

Before going live, ensure:

- [ ] All environment variables are set
- [ ] Database migrations have been run
- [ ] Billing webhooks are configured
- [ ] Custom domain is set up (if using)
- [ ] SSL is enabled (automatic with Railway)
- [ ] Application health checks are passing
- [ ] Monitoring and alerts are configured

## Related Guides

- [Environment Setup](./environment-setup) - Complete environment variable reference
- [Deployment Overview](./deployment) - General deployment guidelines
- [Docker](./docker) - Self-host with Docker instead
