How to Run a Production Postgres and Worker on Railway Safely

October 22, 2026

Running Postgres on Railway in production needs four things the one-click template does not give you: a private network connection, an external backup schedule, a separate worker service, and a restart policy that does not thrash. Each takes a few minutes to configure and none of them is on by default.

Railway is genuinely fine for a database up to a few hundred gigabytes, but the defaults are built for demos. Treat the template as a starting point.

Does Railway’s Postgres Survive a Restart?

Yes, with a caveat. Railway stores the Postgres volume persistently, so data survives a service restart or redeploy. The volume belongs to the service — deleting the Postgres service deletes the data, and there is no undo window.

What does not exist out of the box is point-in-time recovery or an automated off-site backup. On the Pro plan you can enable scheduled volume backups. On Hobby, you write your own dump.

How Do I Connect Services Without Exposing the Database?

The one-click template exposes Postgres publicly by default because it generates a TCP proxy for external tools. If only your own services need access, delete that proxy.

Steps:

  1. Open the Postgres service → Settings → Networking.
  2. Remove the TCP Proxy entry.
  3. Set your app’s DATABASE_URL to the internal hostname, which is the internal host postgres.railway.internal.
  4. Confirm with psql "$DATABASE_URL" -c 'select 1' from a Railway shell.

Internal traffic between services in the same project does not leave Railway’s network and is not billed as egress. Leaving the TCP proxy on means an internet-reachable database on a long random port — not fatal, but unnecessary.

How Do I Schedule Backups on the Hobby Plan?

Pro has backups; Hobby does not. The workaround is a cron service in the same project.

pg_dump "$DATABASE_URL" | gzip | aws s3 cp - s3://your-bucket/db/$(date +%F).sql.gz

Run it as a service with cron schedule 0 3 * * * and restartPolicyType: NEVER. Set a bucket lifecycle rule to expire after 30 days. Test the restore once — an untested backup is a file, not a backup.

How Do I Add a Background Worker?

Railway treats a worker as just another service from the same repo with a different start command. There is no separate worker tier.

Setting Web service Worker service
Start command npm start npm run worker
Public domain Yes No
Health check /health returns 200 None — use a heartbeat row
Restart policy On failure On failure, with backoff

Share the same DATABASE_URL and REDIS_URL by adding the worker to the project; variables set on a shared group are inherited. Without a shared group you will paste connection strings twice and drift.

Why Does My Service Keep Restarting?

Crash loops on Railway usually trace to a memory limit. Hobby containers get a soft ceiling based on plan credits; when a Node process crosses roughly 512 MB of resident memory it gets OOM-killed, restarts, and repeats.

Add a health check path under Settings → Deploy → Healthcheck Path. Railway waits for a 200 before routing traffic, so a slow boot does not serve errors for 40 seconds. Also pin your Node version — an unpinned engines field can pull a major you never tested.

When Should You Not Use Railway for the Database?

If you need multi-region replication, connection pooling at the thousands-of-connections scale, or a compliance audit trail, use a dedicated provider. Railway’s managed Postgres is a convenience layer, not a DBA replacement. Teams that pick it for a regulated workload end up migrating within a quarter.

Quick Summary

  • Railway Postgres persists across restarts but has no automated backup on the Hobby plan.
  • Delete the TCP proxy and use PROJECT.railway.internal hostnames for private traffic.
  • Schedule pg_dump to object storage with a NEVER-restart cron service.
  • Workers are the same repo with a different start command and no public domain.
  • OOM kills at roughly 512 MB are the most common crash-loop cause.
Tags: