X. Deploying to production - LiVanych/locallibrary GitHub Wiki
If you choose a Python/Django-friendly hosting provider they should provide instructions on how to set up a Django website using different configurations of webserver, application server, reverse proxy, etc (this won't be relevant if you choose a PaaS). For example, there are many step-by-step guides for various configurations in the Digital Ocean Django community docs.
Choosing a hosting provider
There are well over 100 hosting providers that are known to either actively support or work well with Django (you can find a fairly extensive list at Djangofriendly hosts).
Some of the things to consider when choosing a host:
-
How busy your site is likely to be and the cost of data and computing resources required to meet that demand.
-
Level of support for scaling horizontally (adding more machines) and vertically (upgrading to more powerful machines) and the costs of doing so.
-
Where the supplier has data centres, and hence where access is likely to be fastest.
-
The host's historical uptime and downtime performance.
-
Tools provided for managing the site β are they easy to use and are they secure (e.g. SFTP vs FTP).
-
Inbuilt frameworks for monitoring your server.
-
Known limitations. Some hosts will deliberately block certain services (e.g. email). Others offer only a certain number of hours of "live time" in some price tiers, or only offer a small amount of storage.
-
Additional benefits. Some providers will offer free domain names and support for SSL certificates that you would otherwise have to pay for.
-
Whether the "free" tier you're relying on expires over time, and whether the cost of migrating to a more expensive tier means you would have been better off using some other service in the first place!
Popular choices in this category include Heroku, Python Anywhere, Amazon Web Services, Microsoft Azure, Digital Ocean, etc.
Getting your website ready to publish
The critical settings that you must check are:
DEBUG
. This should be set as False in production (DEBUG = False
). This stops the sensitive/confidential debug trace and variable information from being displayed.
SECRET_KEY
. This is a large random value used for CSRF protection etc. It is important that the key used in production is not in source control or accessible outside the production server. The Django documents suggest that this might best be loaded from an environment variable or read from a serve-only file.
Open /locallibrary/settings.py
, disable the original SECRET_KEY configuration and add the new lines as shown below in bold. During development no environment variable will be specified for the key, so the default value will be used (it shouldn't matter what key you use here, or if the key "leaks", because you won't use it in production).
# SECURITY WARNING: keep the secret key used in production secret!
# SECRET_KEY = 'cg#p$g+j9ta1$8obt2_+&k3q+pmu)5%asj6yjpkag'
import os
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY',
'cg#p$g+j9ta1$8obt2_+&k3q+pmu)5%asj6yjpkag')
Then comment out the existing DEBUG setting and add the new line shown below.
# SECURITY WARNING: don't run with debug turned on in production!
# DEBUG = True
DEBUG = bool(os.environ.get('DJANGO_DEBUG', True))
The value of the DEBUG
will be True by default, but will be False if the value of the DJANGO_DEBUG
environment variable is set to an empty string, e.g. DJANGO_DEBUG=''
.
A full checklist of settings you might want to change is provided in Deployment checklist (Django docs). You can also list a number of these using the terminal command below:
$ python3 manage.py check --deploy
Installing LocalLibrary on Heroku
In order to execute your application Heroku needs to be able to set up the appropriate environment and dependencies, and also understand how it is launched. For Django apps we provide this information in a number of text files:
runtime.txt: the programming language and version to use.
requirements.txt: the Python component dependencies, including Django.
Procfile: A list of processes to be executed to start the web application.
For Django this will usually be the Gunicorn web application
server (with a .wsgi script).
wsgi.py: WSGI configuration to call our Django application in the Heroku
environment.
Note: The instructions below reflect how to work with Heroku at time of writing. If Heroku significantly change their processes, you may wish to instead check their setup documents: Getting Started on Heroku with Django.
Creating an application repository in Github
Heroku is closely integrated with the git source code version control system, using it to upload/synchronise any changes you make to the live system. It does this by adding a new heroku "remote" repository named heroku pointing to a repository for your source on the Heroku cloud. During development you use git to store changes on your "master" repository. When you want to deploy your site, you sync your changes to the Heroku repository.
Note: Heroku supports only master-to-master branches synhronizations.
Update the app for Heroku
Procfile
Create the file Procfile
(no extension) in the root of your GitHub repository to declare the application's process types and entry points. Copy the following text into it:
web: gunicorn locallibrary.wsgi --log-file -
Django-heroku
$ pip3 install django-heroku
Note: It should be install dj-database-url
and gunicorn
as dependecies.
This is a Django library for Heroku applications that ensures a seamless deployment and development experience.
This library provides:
-
Settings configuration (
Static files
/WhiteNoise
). -
Logging configuration.
-
Test runner (important for Heroku CI).
In settings.py
, at the very bottom:
β¦
# Configure Django App for Heroku.
import django_heroku
django_heroku.settings(locals())
This will automatically configure DATABASE_URL
, ALLOWED_HOSTS
, WhiteNoise
(for static assets), Logging
, and Heroku CI
for your application.
Bonus points! If you set the SECRET_KEY environment variable, it will automatically be used in your Django settings, too!
Disabling Functionality
settings()
also accepts keyword arguments that can be passed False as a value, which will disable automatic configuration for their specific areas of responsibility:
databases
test_runner
staticfiles
allowed_hosts
logging
secret_key
You can also just use this library to provide a test runner for your Django application, for use on Heroku CI:
import django_heroku
TEST_RUNNER = 'django_heroku.HerokuDiscoverRunner'
Serving static files in production
Open /locallibrary/settings.py
and copy the following configuration into the bottom of the file. The BASE_DIR should already have been defined in your file (the STATIC_URL may already have been defined within the file when it was created. While it will cause no harm, you might as well delete the duplicate previous reference).
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
# The absolute path to the directory where collectstatic will
# collect static files for deployment.
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# The URL to use when referring to static files (where they will be served from)
STATIC_URL = '/static/'
requirements.txt
django
gunicorn
django-heroku
Add file requirements.txt in root of your project
runtime.txt
python-3.6.7
Note: Heroku only supports a small number of Python runtimes (at time of writing, this includes the one above). Heroku will use a supported runtime irrespective of the value specified in this file.
Add this file in root of project.
Save changes to Github and re-test
Next lets save all our changes to Github. In the terminal (whist inside our repository), enter the following commands:
$ git add -A
$ git commit -m "Added files and changes required for deployment to heroku"
$ git push origin master
Before we proceed, lets test the site again locally and make sure it wasn't affected by any of our changes above. Run the development web server as usual and then check the site still works as you expect on your browser.
python3 manage.py runserver
We should now be ready to start deploying LocalLibrary
on Heroku.
Create and upload the website
$ heroku create
$ git push heroku master
$ heroku run python manage.py migrate
$ heroku run python manage.py createsuperuser
$ heroku open
Managing addons
You can check out the add-ons to your app using the heroku addons command. This will list all addons, and their price tier and state.
$ heroku addons
Add-on Plan Price State
βββββββββββββββββββββββββββββββββββββββββ βββββββββ βββββ βββββββ
heroku-postgresql (postgresql-flat-26536) hobby-dev free created
ββ as DATABASE
Here we see that we have just one add-on, the postgres SQL database. This is free, and was created automatically when we created the app. You can open a web page to examine the database add-on (or any other add-on) in more detail using the following command:
$ heroku addons:open heroku-postgresql
Other commands allow you to create, destroy, upgrade and downgrade addons (using a similar syntax to opening). For more information see Managing Add-ons (Heroku docs).
Setting configuration variables
$ heroku config
=== locallibrary Config Vars
DATABASE_URL: postgres://uzfnbcyxidzgrl:j2jkUFDF6OGGqxkgg7Hk3ilbZI@ \
ec2-54-243-201-144.compute-1.amazonaws.com:5432/dbftm4qgh3kda3
Note: The secret key needs to be really secret! One way to generate a new key is to use the Django Secret Key Generator.
We set DJANGO_SECRET_KEY using the config:set
command (as shown below). Remember to use your own secret key!
$ heroku config:set DJANGO_SECRET_KEY="eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)"
Setting DJANGO_SECRET_KEY and restarting locallibrary... done, v7
DJANGO_SECRET_KEY: eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh
We similarly set DJANGO_DEBUG:
$ heroku config:set DJANGO_DEBUG=
Setting DJANGO_DEBUG and restarting locallibrary... done, v8
Debugging
The Heroku client provides a few tools for debugging:
# Show current logs
$ heroku logs
# Show current logs and keep updating with any new results
$ heroku logs --tail
# Add additional logging for collectstatic (this tool is run
# automatically during a build)
$ heroku config:set DEBUG_COLLECTSTATIC=1
# Display dyno status
$ heroku ps
If you need more information than these can provide you will need to start looking into Django Logging.
Security settings
Add this environment variables into settings.py
# Security Deployment Vars (for use in dev environment remarked it)
# Only HTTPS ACCESS TO APP
# Disable it for development environment
SECURE_SSL_REDIRECT = True
# May be need to disable it for development environment
SECURE_PROXY_SSL_HEADER=('HTTP_X_FORWARDED_PROTO', 'https')
# May be need to disable it for development environment
SESSION_COOKIE_SECURE = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
CSRF_COOKIE_SECURE = True
# Options bellow can instruct modern browsers to refuse to connect to your
# domain name via an insecure connection (for a given period of time) by
# setting the βStrict-Transport-Securityβ header.
# if it didn't broke anything set 31536000 seconds
SECURE_HSTS_SECONDS = 3600
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
Locally it may be checked with:
./manage.py check --deploy
Then synhronize new settings with heroku repository.
Sendgrid Heroku SMTP Relay configuration
$ heroku addons:create sendgrid:starter
$ heroku config:get SENDGRID_USERNAME
[email protected]
$ heroku config:get SENDGRID_PASSWORD
password
$ heroku config:set EMAIL_HOST_PASSWORD='password'
vim locallibrary/settings.py
...
### EMAIL CONFIGURATION
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.sendgrid.net'
EMAIL_HOST_USER = '[email protected]'
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', '')
EMAIL_PORT = 587
EMAIL_SUBJECT_PREFIX = 'Books Local Library Notification'
EMAIL_USE_TLS = True
SERVER_EMAIL = EMAIL_HOST_USER
### END OF EMAIL CONFIGURATION
...
Synchronize it with heroku
$ git push heroku
See also
Deploying Django (Django docs)
Deployment checklist (Django docs)
Deploying static files (Django docs)
How to deploy with WSGI (Django docs)
How to use Django with Apache and mod_wsgi (Django docs)
How to use Django with Gunicorn (Django docs)
Heroku
Configuring Django apps for Heroku (Heroku docs)
Getting Started on Heroku with Django (Heroku docs)
Django and Static Assets (Heroku docs)
Concurrency and Database Connections in Django (Heroku docs)
How Heroku works (Heroku docs)
Dynos and the Dyno Manager (Heroku docs)
Configuration and Config Vars (Heroku docs)
Limits (Heroku docs)
Deploying Python applications with Gunicorn (Heroku docs)
Deploying Python and Django apps on Heroku (Heroku docs)
Digital Ocean
How To Serve Django Applications with uWSGI and Nginx on Ubuntu 16.04