This guide explains how to deploy QuietPage to Railway.app.
- Railway account (Railway)
- GitHub repository connected to Railway
- Railway CLI (optional, for local testing)
- Log in to Railway dashboard
- Click "New Project"
- Select "Deploy from GitHub repo"
- Choose your QuietPage repository
- Railway will auto-detect the Dockerfile and deploy
- In your Railway project, click "New"
- Select "Database" → "Add PostgreSQL"
- Railway automatically sets
DATABASE_URLenvironment variable
In Railway project settings → Variables, add:
Required variables:
# Django settings
DJANGO_SETTINGS_MODULE=config.settings.production
SECRET_KEY=your-secret-key-here
FERNET_KEY_PRIMARY=your-fernet-key-here
# Domain configuration (use your Railway domain)
ALLOWED_HOSTS=your-app.up.railway.app
CSRF_TRUSTED_ORIGINS=https://your-app.up.railway.app
# Security (Railway handles SSL termination)
SECURE_SSL_REDIRECT=TrueGenerate secret keys:
# Generate Django SECRET_KEY
python config/utils.py
# Generate FERNET_KEY_PRIMARY
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Optional variables:
# Worker concurrency (Railway auto-configures based on memory)
WEB_CONCURRENCY=4
# Email configuration (if using email features)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-email-password
DEFAULT_FROM_EMAIL=noreply@quietpage.comRailway automatically deploys on every git push to your main branch.
Manual deployment:
- Push changes to GitHub
- Railway detects changes and rebuilds
- Health check at
/api/health/must pass before traffic is routed
Check deployment status:
- Railway dashboard shows build logs and deployment status
- Check application logs for errors
Verify deployment:
curl https://your-app.up.railway.app/api/health/
# Should return: {"status": "healthy"}Create superuser (via Railway CLI):
railway run python manage.py createsuperuserOr connect to Django shell:
railway run python manage.py shellRailway uses the multi-stage Dockerfile:
-
Stage 1: Build frontend (Node.js)
- Installs npm dependencies
- Runs
npm run buildto create production bundle
-
Stage 2: Install Python dependencies
- Installs requirements from
requirements/production.txt
- Installs requirements from
-
Stage 3: Combine everything
- Copies frontend build artifacts
- Copies Python dependencies
- Sets up non-root user for security
Railway executes the release command from Procfile before starting the web service:
release: python manage.py migrate --noinput && python manage.py collectstatic --noinput --clearThis ensures:
- Database migrations are applied
- Static files are collected
- Frontend assets are ready to serve
Then starts the web service:
web: gunicorn config.wsgi:application --bind 0.0.0.0:$PORT --workers ${WEB_CONCURRENCY:-4} --timeout 30 --access-logfile - --error-logfile -Railway uses the health check configuration from railway.toml:
[deploy]
healthcheckPath = "/api/health/"
healthcheckTimeout = 100
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 10The /api/health/ endpoint is defined in apps/api/urls.py and returns:
{"status": "healthy"}Defines build and deployment settings:
- Uses Dockerfile for build
- Health check endpoint
- Restart policy
Defines process types:
web: Gunicorn serverrelease: Pre-deployment commands (migrate, collectstatic)
Multi-stage build:
- Frontend build (Node.js)
- Python dependencies
- Final production image
Production Python dependencies including:
dj-database-url==3.1.0- Parse Railway's DATABASE_URLgunicorn==23.0.0- Production WSGI serverpsycopg2-binary- PostgreSQL adapter
Production Django settings:
- DATABASE_URL support via
dj-database-url - Security headers (HSTS, CSP, X-Frame-Options)
- Static file configuration
- CORS and CSRF protection
| Variable | Description | Required | Default |
|---|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes (auto) | - |
PORT |
Web service port | Yes (auto) | 8000 |
DJANGO_SETTINGS_MODULE |
Django settings module | Yes | - |
SECRET_KEY |
Django secret key | Yes | - |
FERNET_KEY_PRIMARY |
Encryption key for journal entries | Yes | - |
ALLOWED_HOSTS |
Allowed domain names | Yes | - |
CSRF_TRUSTED_ORIGINS |
CSRF trusted origins | Yes | - |
SECURE_SSL_REDIRECT |
Redirect HTTP to HTTPS | No | True |
WEB_CONCURRENCY |
Gunicorn worker processes | No | 4 |
EMAIL_HOST |
SMTP server | No | - |
EMAIL_PORT |
SMTP port | No | 587 |
EMAIL_USE_TLS |
Use TLS for email | No | True |
EMAIL_HOST_USER |
SMTP username | No | - |
EMAIL_HOST_PASSWORD |
SMTP password | No | - |
Check build logs in Railway dashboard:
- Frontend build errors: Check
frontend/package.jsonscripts - Python dependency errors: Check
requirements/production.txt - Docker build errors: Check
Dockerfilesyntax
Health check fails:
# Check application logs
railway logs
# Verify DATABASE_URL is set
railway variables
# Test migrations manually
railway run python manage.py migrate --noinputStatic files not found:
# Run collectstatic manually
railway run python manage.py collectstatic --noinput --clear
# Check STATIC_ROOT setting in production.py500 Internal Server Error:
- Check Railway logs for Python tracebacks
- Verify all required environment variables are set
- Check database connection
CSRF verification failed:
- Verify CSRF_TRUSTED_ORIGINS matches your Railway domain
- Ensure ALLOWED_HOSTS includes your Railway domain
- Check that cookies are being sent with API requests
Database connection errors:
- Verify PostgreSQL service is running in Railway
- Check DATABASE_URL environment variable
- Test connection:
railway run python manage.py dbshell
Railway automatically scales based on your plan:
- Starter Plan: 512MB RAM, 1 vCPU
- Developer Plan: 8GB RAM, 8 vCPU
- Team Plan: Custom resources
Adjust worker processes:
# Set in Railway environment variables
WEB_CONCURRENCY=8 # For larger instancesRecommended worker formula:
workers = (2 x CPU cores) + 1
Check logs:
# Real-time logs
railway logs --follow
# Filter by service
railway logs --service webCheck metrics:
- Railway dashboard shows CPU, memory, and network usage
- Set up alerts for resource usage
- Environment Variables: Never commit secrets to git
- HTTPS Only: Railway provides SSL termination automatically
- HSTS Headers: Enabled by default in production settings
- CSP Headers: Content Security Policy configured
- Database Backups: Railway provides automatic backups
- Secret Rotation: Rotate SECRET_KEY and FERNET_KEY_PRIMARY periodically
- Set up custom domain (Railway supports custom domains)
- Configure email provider for password resets
- Set up monitoring and alerting
- Configure database backups
- Review security checklist in
docs/SECURITY_CHECKLIST.md
- Railway Documentation: https://docs.railway.app
- Django Deployment: https://docs.djangoproject.com/en/5.2/howto/deployment/
- Gunicorn Configuration: https://docs.gunicorn.org/en/stable/settings.html