Railway has become the go-to platform for developers who want Heroku-level simplicity with modern infrastructure. For .NET developers, it's the fastest path from a GitHub repo to a running production URL. This guide walks through a complete deployment.
What You Need
- A .NET 8 ASP.NET Core project on GitHub
- A Railway account (free at railway.app)
- Railway CLI (optional but recommended)
Setting Up the Dockerfile
Railway can auto-detect .NET projects, but a Dockerfile gives you full control over the build process. Create one in your project root:
# Dockerfile
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# Copy project file and restore dependencies first (better layer caching)
COPY *.csproj .
RUN dotnet restore
# Copy everything else and build
COPY . .
RUN dotnet publish -c Release -o /app/publish
# Stage 2: Runtime (much smaller image)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
# Non-root user for security
RUN adduser --disabled-password --gecos "" appuser
USER appuser
COPY --from=build /app/publish .
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "MyApp.dll"]Railway sets the PORT environment variable automatically. Configure your app to read it: ASPNETCORE_URLS=http://+:$PORT or use Railway's built-in port detection.
For a minimal API that reads PORT correctly:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Railway sets PORT automatically; fall back to 8080 for local dev
var port = Environment.GetEnvironmentVariable("PORT") ?? "8080";
builder.WebHost.UseUrls($"http://+:{port}");
// ... rest of your app configurationThe railway.json Config File
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "Dockerfile"
},
"deploy": {
"startCommand": "dotnet MyApp.dll",
"healthcheckPath": "/health",
"healthcheckTimeout": 30,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 3
}
}Add a health check endpoint to your app:
// Program.cs
app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow }));Deploying from GitHub (No CLI)
- Go to railway.app and sign in
- Click New Project β Deploy from GitHub repo
- Select your repository
- Railway detects your Dockerfile and starts building
- In ~2 minutes, you have a live URL
That's literally it for a basic deployment.
Railway CLI
The CLI gives you local development, log streaming, and environment management:
# Install
npm install -g @railway/cli
# Authenticate
railway login
# Link to an existing project (from your project directory)
railway link
# Or create a new project
railway init
# Deploy the current directory
railway up
# Watch logs in real-time
railway logs
# Open the running app in browser
railway openDeploying with the CLI
# One-command deploy
railway up
# Deploy a specific service
railway up --service my-api
# Deploy and watch logs
railway up && railway logsEnvironment Variables
Railway's environment variables are set per service per environment (Production, Staging, etc.):
Via Dashboard
In the Railway dashboard: Service β Variables β Add variable
Via CLI
# Set a variable
railway variables set DATABASE_URL="Server=...;Database=...;"
railway variables set ASPNETCORE_ENVIRONMENT="Production"
railway variables set JWT_SECRET="your-secret-here"
# List all variables
railway variables
# Delete a variable
railway variables delete OLD_VARAccessing in Your .NET App
Railway variables are just environment variables β they integrate perfectly with ASP.NET Core configuration:
// appsettings.json values are overridden by environment variables
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
// This reads from ConnectionStrings__DefaultConnection env var (note double underscore)
// Or read directly
var jwtSecret = builder.Configuration["JWT_SECRET"]
?? throw new InvalidOperationException("JWT_SECRET not configured");Adding a PostgreSQL Database
Railway has a native Postgres service that connects to your app automatically:
# Add Postgres to your project
railway add --plugin postgresql
# Railway automatically sets DATABASE_URL in your service's environmentInstall the EF Core PostgreSQL provider:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL// Program.cs β read the DATABASE_URL Railway sets
var databaseUrl = builder.Configuration["DATABASE_URL"]
?? throw new InvalidOperationException("DATABASE_URL not set");
// Parse Railway's postgres:// URL format
var databaseUri = new Uri(databaseUrl);
var userInfo = databaseUri.UserInfo.Split(':');
var connectionString = $"Host={databaseUri.Host};Port={databaseUri.Port};" +
$"Database={databaseUri.AbsolutePath.TrimStart('/')};" +
$"Username={userInfo[0]};Password={userInfo[1]};" +
$"SSL Mode=Require;Trust Server Certificate=true";
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(connectionString));Or use the Npgsql connection string builder:
# Railway also provides individual connection variables:
PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORDvar connectionString = new NpgsqlConnectionStringBuilder
{
Host = builder.Configuration["PGHOST"],
Port = int.Parse(builder.Configuration["PGPORT"] ?? "5432"),
Database = builder.Configuration["PGDATABASE"],
Username = builder.Configuration["PGUSER"],
Password = builder.Configuration["PGPASSWORD"],
SslMode = SslMode.Require
}.ConnectionString;Custom Domains
# Add a custom domain via CLI
railway domain --service my-api add www.yourdomain.com
# Railway provides the CNAME to point at your domain registrarOr via dashboard: Service β Settings β Domains β Custom Domain.
Railway provides TLS certificates automatically for all custom domains.
Environments (Staging / Production)
Railway supports multiple environments per project:
# Create a staging environment
railway environment create staging
# Deploy to staging
railway up --environment staging
# Switch CLI context to staging
railway environment stagingEach environment has its own set of variables, databases, and deployments. Your staging environment can mirror production with a separate database.
Pricing
Railway uses a usage-based pricing model:
| Plan | Monthly Base | Compute | Memory |
|---|---|---|---|
| Trial | $5 credit (once) | $0.000463/vCPU-minute | $0.000231/GB-minute |
| Hobby | $5/month included | Same rates | Same rates |
| Pro | $20/month included | Same rates | Same rates |
Typical costs for a small .NET API:
A minimal .NET API using ~0.1 vCPU and 256 MB RAM continuously:
- Compute: 0.1 Γ 43,800 min Γ $0.000463 β $2.03/month
- Memory: 0.25 Γ 43,800 min Γ $0.000231 β $2.53/month
- Total: ~$4.56/month (within Hobby plan's $5 credit)
Adding Postgres: $0.000231/GB-minute for storage + compute for the DB instance.
Most small .NET APIs with a Postgres database run comfortably within Railway's $5/month Hobby plan. Estimate your costs at railway.app/pricing before scaling up.
Automatic Deployments
By default, every push to your main branch triggers a deployment. Configure branch-based deployments:
In the dashboard: Service β Settings β Deployments:
- Watch Branch:
main(or any branch) - Root Directory:
/or a subdirectory if your .NET project isn't at the repo root
Pros and Cons
Pros
- Zero config for basics β push code, get URL, no YAML manifests
- Native Git integration β auto-deploy on push
- Excellent DX β the dashboard is clean and intuitive
- Native Postgres, Redis, MySQL β one click to add a database
- Fair pricing β usage-based, easy to estimate
- Custom domains with auto-TLS β included for all plans
- Environment branching β staging/production separation built in
Cons
- No SLA on Hobby plan β Pro plan required for uptime guarantees
- US-centric regions β fewer regions than AWS/Azure (though expanding)
- Limited enterprise features β no VPCs, private networking on lower plans
- Build minutes can add up β large .NET solutions with many projects take time
- No auto-scaling β vertical scaling only (upgrade instance size); horizontal scaling is manual
When to Choose Railway
Railway is the right choice when:
- You're a solo developer or small team
- You want to deploy quickly without infrastructure expertise
- Your app is a standard web API + database combination
- Budget is a concern and you want predictable, low costs
- You're prototyping or running a side project
Migrate off Railway when:
- You need compliance guarantees (SOC2, HIPAA with BAA, etc.)
- You need complex networking (VPCs, private endpoints)
- You need multi-region active-active deployments
- Your traffic patterns require sophisticated auto-scaling