MMakerToLaunch

Deploy

Deploy Django on Railway (Beginner Guide)

Django is one of the most common backends in AI-assisted projects — and Railway is one of the friendliest places to host it. This guide walks you through the full production checklist: PostgreSQL, environment variables, security settings, migrations, static files, and the error messages beginners hit most often. No DevOps background required.

Updated July 2026 · 10 min read

Written for AI makers using tools like Cursor, Bolt, Lovable, Replit, Claude, and ChatGPT.

Quick answer

To deploy Django on Railway: push code to GitHub, create a Railway project with PostgreSQL, set SECRET_KEY, DEBUG=False, ALLOWED_HOSTS, and DATABASE_URL as environment variables, run migrations, configure static files (usually WhiteNoise), and deploy. Railway provides DATABASE_URL automatically when you add a Postgres plugin.

Who this is for

  • You have a Django app that runs locally with python manage.py runserver and want it live on the internet
  • Cursor or another AI tool generated a Django project and you need production settings explained
  • You tried deploying and got DisallowedHost, database connection, or static file errors

What you'll need

  • Django project on GitHub (no .env or secrets in the repo)
  • Railway account (free tier works for testing)
  • Roughly 30–60 minutes for your first deploy

What Railway is

Railway is a cloud hosting platform that runs your code from a GitHub repository. Unlike static hosts, Railway supports long-running processes — which is what Django needs. You push code, Railway builds a container, installs dependencies from requirements.txt, and runs your start command.

Railway also offers one-click PostgreSQL databases. When you add Postgres to a project, Railway injects a DATABASE_URL environment variable that points to your production database. This is much simpler than managing a database server yourself.

Pricing starts with a free trial credit. Small Django apps with modest traffic often stay within low monthly costs. You pay for compute and database usage as you grow.

Railway fits Django, FastAPI, Flask, Node APIs, and more. If your AI tool built a Python backend with manage.py in the root, Railway is a strong first choice.

Django production checklist (before Railway)

Deploying without fixing production settings is the number-one reason Django apps crash on Railway. Work through this checklist on your local project first — or use the AI prompt at the bottom of this guide.

  • DEBUG = False in production settings
  • SECRET_KEY read from environment variable, not hardcoded
  • ALLOWED_HOSTS includes your Railway domain (and custom domain if used)
  • DATABASES configured via DATABASE_URL (dj-database-url or django-environ)
  • Static files configured (WhiteNoise is the beginner-friendly default)
  • requirements.txt lists all dependencies including gunicorn and psycopg2-binary
  • A Procfile or start command uses gunicorn, not runserver
  • .env in .gitignore.env.example committed instead

PostgreSQL and DATABASE_URL

Django's default SQLite database works on your laptop but is wrong for Railway production. SQLite files do not persist reliably on ephemeral containers, and concurrent writes cause problems. Use PostgreSQL in production.

In Railway, click New → Database → PostgreSQL. Railway creates the database and adds DATABASE_URL to your project's environment variables. The value looks like postgres://user:password@host:port/dbname.

In Django settings, parse DATABASE_URL with dj-database-url or django-environ. A common pattern is: import dj_database_url; DATABASES = {'default': dj_database_url.config(default=os.environ.get('DATABASE_URL'))}.

Never commit DATABASE_URL to GitHub. It contains credentials. Railway sets it for you — your code just reads os.environ.

After the first successful deploy, run python manage.py migrate on Railway to create tables. Railway lets you run one-off commands from the dashboard or via railway run.

SECRET_KEY

Django's SECRET_KEY signs sessions, cookies, and CSRF tokens. The default key in a new project is insecure and must not be used in production.

Generate a new key: python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())". Copy the output.

In Railway, add an environment variable SECRET_KEY with that generated value. In settings.py, read it: SECRET_KEY = os.environ['SECRET_KEY'] or os.environ.get('SECRET_KEY', 'dev-only-fallback').

If you ever expose SECRET_KEY in GitHub, generate a new one immediately. Attackers can forge sessions with a leaked key.

DEBUG = False

With DEBUG=True, Django shows detailed error pages with stack traces, settings snippets, and SQL queries to anyone who triggers an error. That is helpful on your laptop and dangerous on the internet.

Set DEBUG = os.environ.get('DEBUG', 'False') == 'True' or use separate settings modules for development and production. In Railway, either omit DEBUG (default False) or set DEBUG=False explicitly.

When DEBUG is False, you also need ALLOWED_HOSTS configured and proper static file handling — otherwise you get different errors that confuse beginners. Fix ALLOWED_HOSTS and static files in the same pass.

Also set a proper logging configuration so you can still diagnose errors without showing them to users. Railway's deploy logs capture stdout — use logging.info and logging.error in your views.

ALLOWED_HOSTS

Django checks the Host header on every request. If the domain is not in ALLOWED_HOSTS, you get DisallowedHost at / — one of the most common Railway Django errors.

Railway gives your app a domain like yourapp.up.railway.app. Add that exact hostname to ALLOWED_HOSTS. Example: ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost').split(',') with ALLOWED_HOSTS=yourapp.up.railway.app in Railway.

If you add a custom domain later, append it to the comma-separated list. Do not use ALLOWED_HOSTS = ['*'] in production — it is a security risk.

The error page literally says 'Invalid HTTP_HOST header' or lists the host you need to add. Copy the hostname from the error into ALLOWED_HOSTS and redeploy.

Running migrations on Railway

Your local database has tables from running migrate locally. Production PostgreSQL starts empty. You must run migrations against the production database after deploy.

Options on Railway: add python manage.py migrate to your release/start script, use Railway's one-off command feature, or add a release phase in railway.toml if configured.

A simple approach for beginners: add migrate to your start command: python manage.py migrate && gunicorn myproject.wsgi. This runs migrations on every deploy, which is acceptable for small apps.

Verify migrations worked by checking Railway logs for migration output and testing create/read flows on your live URL.

Static files (CSS, JavaScript, admin styles)

With DEBUG=False, Django does not serve static files automatically. Without configuration, your admin panel and CSS look broken in production.

WhiteNoise is the standard beginner solution. Add whitenoise to requirements.txt, add WhiteNoiseMiddleware after SecurityMiddleware in settings.py, and set STATIC_ROOT = BASE_DIR / 'staticfiles'. Run collectstatic during build or start.

A typical start script: python manage.py collectstatic --noinput && gunicorn myproject.wsgi. Some projects run collectstatic in a build step instead.

For apps with heavy media uploads (user images), you may eventually need S3 or similar object storage. For first launch, WhiteNoise handles CSS/JS/admin assets.

Railway deploy walkthrough

Sign up at railway.app and connect your GitHub account. Click New Project → Deploy from GitHub repo → select your Django repository.

Add PostgreSQL from the project canvas (+ New → Database → PostgreSQL). Railway links DATABASE_URL to your web service automatically.

Click your web service → Variables → add SECRET_KEY, ALLOWED_HOSTS, and any third-party keys (email, Stripe, OpenAI, etc.). Reference your .env.example for the full list.

Set the start command if Railway does not detect it: gunicorn myproject.wsgi --bind 0.0.0.0:$PORT. Replace myproject with your actual project name. Railway sets the PORT variable.

Deploy, watch the build logs, and open the generated URL. If you see errors, scroll to the bottom of the log — the last traceback line is usually the fix hint.

Common Railway Django errors

These errors account for most beginner support threads. Match your symptom to the fix.

  • DisallowedHost → add your Railway domain to ALLOWED_HOSTS env var and redeploy
  • Application failed to respond → wrong start command, app not binding to 0.0.0.0:$PORT, or crash on startup
  • relation does not exist → migrations not run on production database
  • no such table → still using SQLite or migrations missing
  • Static files 404 / broken admin CSS → enable WhiteNoise and run collectstatic
  • ImproperlyConfigured SECRET_KEY → set SECRET_KEY in Railway variables
  • ModuleNotFoundError gunicorn → add gunicorn to requirements.txt
  • psycopg2 errors → add psycopg2-binary to requirements.txt for PostgreSQL

Step-by-step

  1. 1

    Step 1

    Prepare Django for production

    Set DEBUG=False via env, externalize SECRET_KEY, configure DATABASE_URL parsing, add WhiteNoise, and update requirements.txt with gunicorn and psycopg2-binary.

  2. 2

    Step 2

    Push to GitHub safely

    Confirm .env is gitignored. Commit .env.example. Push to GitHub and verify no secrets in the repo.

  3. 3

    Step 3

    Create Railway project + PostgreSQL

    New Project from GitHub, add PostgreSQL plugin, confirm DATABASE_URL appears in variables.

  4. 4

    Step 4

    Set environment variables

    Add SECRET_KEY, ALLOWED_HOSTS (your Railway hostname), and any API keys from .env.example.

  5. 5

    Step 5

    Configure start command and deploy

    Use gunicorn binding to $PORT. Include migrate and collectstatic if needed. Deploy and check logs.

  6. 6

    Step 6

    Test on live URL

    Open the Railway URL, test admin, forms, and database writes. Fix ALLOWED_HOSTS or static issues if needed.

Common mistakes

  • Deploying with DEBUG=True

    How to fix it: Read DEBUG from environment, default to False in production. Never expose stack traces publicly.

  • Using SQLite in production on Railway

    How to fix it: Add Railway PostgreSQL, set DATABASE_URL, install psycopg2-binary, and update DATABASES in settings.

  • Forgetting ALLOWED_HOSTS

    How to fix it: Set ALLOWED_HOSTS to your Railway domain (e.g. myapp.up.railway.app) before first deploy.

  • Using runserver as the start command

    How to fix it: Use gunicorn myproject.wsgi --bind 0.0.0.0:$PORT for production.

  • Skipping migrations in production

    How to fix it: Run python manage.py migrate against the Railway database before testing features.

  • No static files configuration

    How to fix it: Add WhiteNoise, STATIC_ROOT, and collectstatic to your build or start process.

Copy-ready prompt

Prompt: prepare Django app for Railway

Paste this into Cursor, Claude, ChatGPT, Bolt, Lovable, or your AI coding tool.

Ready to paste

Prepare this Django project for deployment on Railway.

1. Configure settings to read SECRET_KEY, DEBUG, ALLOWED_HOSTS, and DATABASE_URL from environment variables.
2. Set up PostgreSQL via DATABASE_URL using dj-database-url or django-environ (not SQLite for production).
3. Add gunicorn, whitenoise, psycopg2-binary, and dj-database-url to requirements.txt if missing.
4. Configure WhiteNoise for static files with STATIC_ROOT and collectstatic.
5. Create or update .env.example with all required variables and placeholder values.
6. Confirm .env is in .gitignore.
7. Give me the exact Railway start command (gunicorn binding to $PORT).
8. List any hardcoded localhost URLs or security issues that would break in production.

Do not commit real secrets. DEBUG must default to False for production.

Paste this into Cursor, Claude, ChatGPT, Bolt, Lovable, or your AI coding tool.

Ready check

Beginner checklist

  • DEBUG=False in production (via environment variable)
  • SECRET_KEY set in Railway variables — not hardcoded in settings.py
  • ALLOWED_HOSTS includes your Railway domain
  • PostgreSQL added on Railway — DATABASE_URL present
  • requirements.txt includes gunicorn, whitenoise, psycopg2-binary
  • Start command uses gunicorn on 0.0.0.0:$PORT
  • Migrations run on production database
  • Static files configured (WhiteNoise + collectstatic)
  • .env.example committed — .env not on GitHub
  • Live URL tested — admin, forms, and database writes work

Frequently asked questions

Why does my Django app work locally but not on Railway?
Usually DEBUG=True locally with different settings, missing ALLOWED_HOSTS, unset DATABASE_URL, or SQLite instead of PostgreSQL. Check Railway deploy logs for the exact traceback.
Does Railway provide DATABASE_URL automatically?
Yes, when you add a PostgreSQL database to your Railway project. Link it to your web service and DATABASE_URL appears in environment variables — no manual connection string assembly needed.
Do I need Docker to deploy Django on Railway?
No. Railway builds from your requirements.txt and start command using Nixpacks. Docker is optional for advanced setups.
How do I run Django admin in production?
Run python manage.py createsuperuser via Railway's shell or one-off command. Access /admin/ on your live URL. Ensure static files work so the admin CSS loads.
Railway vs Vercel for Django?
Railway runs persistent Django processes and PostgreSQL natively. Vercel is optimized for serverless frontends. Django on Vercel requires extra configuration and is not the beginner path.