Deploy Django REST | API with Postgres and a Generated Secret Key
Django 6 and DRF on Railway, with a signing key generated per deployment
Just deployed
/var/lib/postgresql/data
Just deployed
Deploy and Host a Django REST API on Railway
Django 6 and Django REST Framework on Postgres, with a signing key generated per deployment.
About Hosting Django on Railway
The Django REST template most people deploy was last touched in September 2023 - Django 4.2, DRF 3.14, psycopg2 - and none of its deployments report healthy. But the stale versions are not the worst part.
Its SECRET_KEY is a literal, committed into the template. Every project deployed from it shares the same Django signing key, and that template is public. Anyone who reads it can forge session cookies and password-reset tokens against every site created from it.
Here SECRET_KEY is generated per deployment. The settings module reads it with os.environ["SECRET_KEY"] rather than os.environ.get(..., "fallback"), so a missing key stops the app at startup instead of quietly falling back to a shared default - which is how a template ends up shipping one key to everybody in the first place.
Common Use Cases
- A JSON API with a relational model, an admin UI and migrations from day one
- The backend for a front end you host separately
- An internal tool where the Django admin is most of the product
Dependencies for Django Hosting
- Postgres, included, on a persistent volume
- Nothing else. Without DATABASE_URL it falls back to SQLite, so local development needs no database.
Deployment Dependencies
- Django: https://docs.djangoproject.com
- Django REST Framework: https://www.django-rest-framework.org
- Source: https://github.com/ak40u/django-rest-railway-starter
Implementation Details
| Method | Path | Does |
|---|---|---|
| GET | /api/health/ | Runs SELECT 1; 503 when Postgres is unreachable |
| GET | /api/notes/ | Paginated list |
| POST | /api/notes/ | Creates a note - requires authentication |
| - | /admin/ | Django admin |
Create the first user after deploying, from the service shell: python manage.py createsuperuser.
Four settings exist because of how Railway runs the app, and each one costs an afternoon to rediscover:
- SECURE_PROXY_SSL_HEADER. TLS terminates at the platform proxy. Without this Django believes every request is plain HTTP and builds http:// redirects that break the admin login.
- ALLOWED_HOSTS includes healthcheck.railway.app. The health checker reaches the container over the internal network, so its requests carry a different Host header. Without that entry Django answers the health check with 400 and the deployment is marked failed while the app is running perfectly well.
- collectstatic runs in the build, not in pre-deploy. The pre-deploy step runs in a separate throwaway container; files it writes never reach the running app, and Django then warns "No directory at: /app/staticfiles/" and returns 500 for the admin.
- migrate runs in pre-deploy, not in the build. The build has no database to connect to.
Why Deploy Django on Railway?
A database, a domain and TLS from one deploy, migrations applied before each release takes traffic, and a health check that fails when the database is gone rather than when Django is merely unhappy about a header.
Template Content