Migrating a Django App from Heroku to Render
I recently migrated a Django application from Heroku to Render. They are similar enough platforms that it was a pretty straightforward and smooth process but there were a couple things that caused some confusion and minor setbacks along the way. Hopefully this post will make the process even easier for you (or myself) in the future.
Render has pretty good guides for setting up projects on their platform. To migrate my Django app, I followed a process that was a combination of their deploying Django and Migrating from Heroku guides. I’ll describe my process below and call out a couple parts of the process that had I known more ahead of time, the process would have been even smoother.
Step 1: Set up the render.yaml file
The render.yaml
file is Render’s Infrastructure-as-code spec. My approach was to replicate my Heroku app service descriptions and settings in the render.yaml
file and include it in the root of my repo. The example from the deploying Django offered a good starting point. I added a Redis service instance for caching and then all the environment variables from my Heroku configuration. For the envVars
section of the web service in the render.yaml
file, the DATABASE_URL
and REDIS_URL
values were set up to pull the connection parameters from the Postgres and Redis settings using fromDatabase
and fromService
respectively. The other variables were set up with sync: False
which will prompt the values to be inputted when the blueprint is deployed for the first time.
Here’s roughly what my render.yaml
file looked like:
services:
- type: web
name: margins-api-django
env: python
region: ohio
plan: starter
branch: main
buildCommand: "./build.sh"
startCommand: "gunicorn margins.wsgi:application"
healthCheckPath: /health
envVars:
- key: DATABASE_URL
fromDatabase:
name: margins-prod
property: connectionString
- key: REDIS_URL
fromService:
type: redis
name: margins-cache
property: connectionString
- key: LOGGING_LEVEL
sync: false
- key: SECRET_KEY
sync: false
- key: SENTRY_DSN
sync: false
- key: DEBUG
value: off
- key: PYTHON_VERSION
value: "3.9.13"
- type: redis
name: margins-cache
ipAllowList:
- source: 0.0.0.0/0
description: everywhere
plan: free
region: ohio
databases:
- name: margins-prod
plan: free
region: ohio
databaseName: margins
user: margins
A note about the Python version: as of this writing, Render defaults to Python 3.7 but this can be overridden by including the PYTHON_VERSION
environment variable in web service.
Step 2: Deploy the blueprint
On the Blueprints section of the Render dashboard, click the “New Blueprint Instance” button and select your Github repo. You will be prompted to enter values for any environment variables where you set sync: false
as described above.
If everything was configured correctly, you should see your web service and Postgres and Redis services spin up successfully.
Step 3: Migrate postgres data
If everything goes well with the first two steps, you should have a working web application but with an empty database. Copying over the data from your Heroku database is probably the trickiest part of this process and potentially the most stressful if your application has a lot of users and traffic.
The copy data from Postgres section of the Migrating from Heroku article is a really good walkthrough and I generally followed those same steps but there is one key thing I did differently.
The last step where the pg_restore
command is run likely works well for a completely empty database however, per the deploying Django guide as well as how my Heroku app was set up, database migrations are run during the build step of the web app deployment process meaning the database has tables set up already even though most of them are empty. This caused me to get a lot of errors and missing data when I ran the pg_restore
command the first time. What I did instead which worked great was to drop the database and then create it again and then run the pg_restore
command on the new, completely empty database. Specifically, I followed the below steps:
- Log into your newly-created Postgres instance. Render makes this really easy by generating a full
psql
command for you that’s specific to your database. You can find it on your Postgres instance page from the Render dashboard under the section, “Connecting from outside the cluster”. - Drop the DB. First, make note of the database name. It was the last part of the
psql
command or you can find it next to the “Database” parameter (below the port number) on the Render dashboard. Frompsql
, runDROP DATABASE <database_name>;
- Re-create the DB. Run
CREATE DATABASE <database_name>;
- Quit the postgres terminal by running
\q
- Run the
pg_restore
command. The version I used was slightly different from the one in the guide in that it included the database username as specified in the blueprint although I’m not 100% if that was necessary:pg_restore --verbose --no-acl --no-owner -d <EXTERNAL CONNECTION STRING> -U <YOUR USER> latest.dump
Step 4: Update DNS Config
The Render guide covers this part pretty well.
Once the DNS settings propagate, you should hopefully have an application that’s running smoothly on Render! Render is still a pretty new service but I’ve been really happy with it so far.
If you performed a similar migration and have any questions or additional tips, get in touch!.