← Back to articles

Self-Hosting n8n on Railway for $5/Month: The Setup I Actually Use in Production

Skip the $20/month n8n cloud plan. Here's how I run my own n8n instance on Railway with persistent storage, webhooks, and automatic restarts — all for under $5/month.

Self-Hosting n8n on Railway for $5/Month: The Setup I Actually Use in Production

I've been running n8n in production for about two years now. For the first year, I was on their cloud plan — it's fine, the UI is fast, support is decent. But $20/month for a tool I use for automating my own freelance workflows started feeling like a lot, especially when most of what I'm doing could run on a $5 VPS.

I switched to self-hosting on Railway about 14 months ago. The setup took maybe two hours the first time, and I've touched the infrastructure almost zero times since. This is that setup.

Why Self-Host At All?

The honest answer: cost and control.

n8n Cloud starts at $20/month for the Starter plan. That gets you 2,500 workflow executions per month. If you're running anything more than hobby automations — scheduled scrapers, webhook handlers, multi-step AI pipelines — you'll hit that ceiling fast. The next tier jumps to $50/month.

Self-hosting gives you unlimited executions. You're only constrained by the compute you throw at it, and for most solo developer workloads, a 512MB RAM container is more than enough.

The other reason is data. Some of my workflows touch client API keys, invoice data, and internal tooling. I'd rather that not sit in someone else's execution logs.

What You Need

  • A Railway account (free tier is fine to start)
  • A domain or subdomain you control (for webhooks to work properly)
  • About 30 minutes

That's it. No Docker expertise required — Railway handles the container runtime. n8n publishes an official Docker image that works out of the box.

Step 1: Create a New Railway Project

Go to your Railway dashboard and click New Project → Deploy from Docker Image.

Use this image:

n8nio/n8n:latest

Railway will spin up a container. It'll fail immediately because n8n needs some environment variables, but that's expected. Go to the Variables tab before worrying about it.

One thing I'd recommend: don't use latest for production indefinitely. Once your setup is stable, pin to a specific version like n8nio/n8n:1.85.0. Unexpected upgrades have broken my workflows twice when n8n changed internal behavior between minor versions.

Step 2: Environment Variables

This is where most guides get lazy and list 40 variables without explaining what matters. Here's what you actually need:

# Core — required
N8N_HOST=0.0.0.0
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://your-n8n-subdomain.up.railway.app/

# Basic auth — don't skip this
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=pick_something_strong

# Execution settings
EXECUTIONS_PROCESS=main
N8N_DIAGNOSTICS_ENABLED=false
N8N_VERSION_NOTIFICATIONS_ENABLED=false

# Timezone
GENERIC_TIMEZONE=Europe/Warsaw
TZ=Europe/Warsaw

# Database (covered in Step 3)
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=your_postgres_host
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=postgres
DB_POSTGRESDB_PASSWORD=your_db_password

A few notes on these:

N8N_BASIC_AUTH_ACTIVE — n8n has its own user management system now, but I still enable basic auth as a first layer. The built-in login screen is nice, but adding HTTP basic auth means bots get rejected before they even see the n8n UI.

EXECUTIONS_PROCESS=main — By default n8n spawns a child process for each execution. On a memory-constrained container, this causes OOM kills. Setting it to main runs everything in the same process. Slower in theory, but in practice on Railway's 512MB tier it's rock solid.

N8N_DIAGNOSTICS_ENABLED=false — Disables telemetry. Your call, but I turn it off by default on self-hosted installs.

Step 3: Persistent Storage with PostgreSQL

By default n8n uses SQLite stored in the container filesystem. On Railway, containers are ephemeral — every deploy wipes the filesystem. If you don't fix this, you lose all your workflows on every deploy. This trips up a lot of people.

The fix: use PostgreSQL for n8n's database.

In Railway, click New → Database → PostgreSQL. Railway provisions a Postgres instance and gives you connection details. Grab the DATABASE_URL and parse it into the individual DB_POSTGRESDB_* variables from the list above.

Why not just use Railway's volume mounts for SQLite? You can, and it works. But PostgreSQL gives you better query performance on larger execution histories, and it's easier to back up. Railway's Postgres instances also have automatic daily backups on paid plans.

Once you've set the DB variables, redeploy your n8n service. On first boot, n8n will run its migrations and create all the necessary tables. You should see this in the logs:

Database migrations running...
Migrations finished.
n8n ready on 0.0.0.0, port 5678

Step 4: Webhooks and Custom Domain

Webhooks are the main reason you need a stable URL. If your n8n instance restarts and gets a different Railway URL, any external service pointing at your webhook breaks.

Option A: Use Railway's generated domain

Railway gives each service a .up.railway.app subdomain. It's permanent as long as the service exists, so for most personal setups this is fine. Set WEBHOOK_URL to this URL.

Option B: Custom domain

If you want a clean URL like n8n.yourdomain.com, go to your Railway service → Settings → Networking → Custom Domain. Add your subdomain, then add the CNAME record Railway gives you to your DNS provider.

DNS propagation takes anywhere from 2 minutes to a few hours. Once it's live, update WEBHOOK_URL and N8N_HOST to your custom domain.

I use a custom domain purely for aesthetics and so webhook URLs in external services look consistent. There's no functional difference.

Step 5: Email Notifications (Optional But Useful)

n8n can send you an email when a workflow errors. If you're running anything critical, set this up.

N8N_EMAIL_MODE=smtp
N8N_SMTP_HOST=smtp.gmail.com
N8N_SMTP_PORT=465
N8N_SMTP_USER=your@gmail.com
N8N_SMTP_PASS=your_app_password
N8N_SMTP_SENDER=your@gmail.com
N8N_SMTP_SSL=true

For Gmail, you need to generate an App Password (not your regular password). Go to Google Account → Security → App Passwords. Takes two minutes.

Once configured, go to Settings → User Management in n8n and make sure your user has an email attached. n8n sends error notifications to the workflow owner's email.

Cost Breakdown

Here's my actual Railway bill for a typical month:

| Resource | Cost | |---|---| | n8n service (512MB RAM, 1 vCPU) | ~$3.20 | | PostgreSQL (512MB storage) | ~$1.50 | | Total | ~$4.70/month |

Railway bills on actual usage, not a flat rate for the container size. If you're running low traffic, it's often even cheaper. My heaviest month was $6.40 when I had a scraping workflow running every 10 minutes around the clock.

Compare this to n8n Cloud's $20 Starter plan or $50 Pro plan, and the math is obvious. If you run n8n for a full year, self-hosting on Railway saves you between $180 and $540.

What I Actually Automate With This

Some of the workflows I run on this instance, to give you a sense of the scale:

Lead enrichment — A webhook receives new Typeform submissions, calls the Clearbit API to enrich the contact data, creates a row in a Notion database, and sends me a Slack message with a summary. Runs maybe 50-100 times a month.

Invoice reminders — Every Monday morning, a cron workflow checks my Notion invoice tracker for anything overdue by more than 14 days and sends a templated email reminder. This one alone has saved me thousands in late payments.

GitHub PR digest — A scheduled workflow polls the GitHub API for PRs opened across my client repos in the last 24 hours and sends a morning digest to Slack. Simple but useful for tracking freelance work.

AI content pipeline — This is the heavier one. A webhook receives a topic, calls the Anthropic API to generate a draft, reformats it, saves it to a Supabase database, and triggers a Slack notification with a preview link. This workflow alone runs several hundred executions a month — well over n8n Cloud's Starter limit.

Production Tips From Running This for 14 Months

Pin your n8n version. Seriously. When a major version drops, read the migration notes before upgrading. n8n 1.0 broke several of my HTTP Request nodes because of how it handled authentication headers.

Set up workflow error handling globally. In n8n, you can define an "Error Workflow" that triggers whenever any other workflow fails. I route all errors to a Slack channel. This is infinitely better than checking logs manually.

Back up your workflows independently. Even with PostgreSQL, I export my workflows as JSON every couple of weeks using n8n's built-in export endpoint:

curl -u admin:your_password https://your-n8n-url/api/v1/workflows \
  -H "Accept: application/json" > workflows_backup.json

Stick this in a cron job on any machine you have access to and sleep better at night.

Don't run heavy workflows during business hours if you're on a shared Railway instance. Railway's free and Hobby plans share underlying hardware. For anything that runs CPU-intensive tasks for more than a few seconds, either upgrade to a dedicated plan or schedule those workflows for off-peak hours.

Is Railway the Right Choice?

For this use case, yes — but it's worth knowing the alternatives.

Railway is my pick because the developer experience is genuinely good, the pricing is predictable, and it takes zero DevOps knowledge to run a container. If you already have a VPS on DigitalOcean or Hetzner, you can self-host there too — the n8n Docker setup is identical, you just manage Nginx and SSL yourself.

The Railway Hobby plan ($5/month base + usage) is the sweet spot for solo developer workloads. If you hit the limits, the Pro plan at $20/month is still cheaper than n8n Cloud for anything beyond the Starter tier.


Some links in this post are affiliate links. If you sign up for Railway or Supabase through my links, I get a small commission at no extra cost to you. I only link to tools I actually pay for and use.