Making your deployment production-ready
Additional tools for your Django app production environment.
From this section onwards, the code is pushed into the production branch.
Gunicorn
For a production environment, it's advisable to use a production-grade WSGI server like Gunicorn, as Django's default server is not suitable for production settings.
Moreover, it's advisable to integrate Nginx as a reverse proxy for Gunicorn, which will also manage the serving of static files.
Open Python Packages Tool → Window View | Tool Windows | Python Packages and install “gunicorn”
Make sure to add gunicorn
to requirements.txt
.
Environment Variables
Ensure that secret information is passed via environment variables rather than hardcoding it, to avoid compromising security.
Create a .env
file and add the following information, make sure you don’t commit this file into git.
Next, update the Docker Compose file and run it to check everything is working fine as expected.
On line 5, I included dockerfile: Dockerfile
. This indicates the specific Dockerfile to build the image. In this scenario, it's titled "Dockerfile" and resides in the same directory. Although not mandatory, if you are using a different name for the Dockerfile, then you must specify it.
Cool! Everything seems to be working fine, without any hiccups.
PostgreSQL
When considering setting something up in production, we undoubtedly prioritize employing a robust, battle-tested database. Postgres is unquestionably one of the most popular relational databases.
If you are considering trying out Postgres on your local machine before moving ahead with Docker, then run the following command.
I will show you how to do it with Docker shortly.
First, install the psycopg2 binary. After installation make sure to update the requirements.txt
file
pip install psycopg2-binary
Before proceeding, make sure you have installed the libpq
driver in your machine.
References:
Next, navigate to the DATABASES
section in settings.py
and modify the configuration to receive data from environment variables.
Next, create a db.env
file which contains environment variables for the postgres container.
Update the .env
file with postgres environment variables.
Update the compose.yaml
file with the postgres configuration.
services:
server:
build:
context: .
dockerfile: Dockerfile
ports:
- 8000
volumes:
- static_files:/app/staticfiles
env_file:
- .env
depends_on:
db:
condition: service_healthy
networks:
- django_docker_net
db:
image: postgres:16.1
restart: always
expose:
- 5432
healthcheck:
test: [ "CMD", "pg_isready" ]
interval: 10s
timeout: 5s
retries: 5
env_file:
- db.env
networks:
- django_docker_net
nginx:
image: nginx:1.25
ports:
- "8080:80"
volumes:
- ./nginx/custom-nginx.conf:/etc/nginx/conf.d/default.conf
- static_files:/app/staticfiles
depends_on:
- server
networks:
- django_docker_net
networks:
django_docker_net:
driver: bridge
volumes:
db-data:
static_files:
Also, make sure to add this line in your Dockerfile
.
RUN apt-get update && apt-get install -y libpq-dev
NOTE:
libpq-dev
is Psycopg2 binary dependencies. These files are necessary for compiling and linking applications that use PostgreSQL.
NOTE: You may run into build-related issues. However, if you need to compile or build something that requires additional development tools (such as PostgreSQL extensions or other native modules), you should consider including
build-essential
.When you installbuild-essential
, it installs gcc, g++, make, and other basic tools that are commonly needed to compile software.
RUN apt-get update && apt-get install -y build-essential libpq-dev
Make sure to expose the gunicorn in Dockerfile
so NGINX can proxy the request. We'll delve into NGINX shortly.
Once done, update Run Configuration for Docker Compose.
We are making sure that the container should always build whenever we run through Docker Compose.
Provide the path to the compose.yaml
file and then go ahead and click Apply -> Run.
It will take a couple of minutes to initialize, and this is how the final output will look.
Next, right-click on the “django_docker-server-1” container and choose Create Terminal
Inside the terminal, we will be migrating the database files.
NGINX
NGINX is a high-performance web server, known for its stability, rich feature set, simple configuration, and low resource consumption. It's widely used for serving static content, reverse proxying, load balancing, and as an HTTP cache.
We will utilize NGINX as a reverse proxy in conjunction with Gunicorn to manage client requests and handle static files.
We are going to add nginx section in compose.yaml
.
Additionally, we will replace the default nginx configuration with our own, naming it custom-nginx.conf
.
Generate a file titled custom-nginx.conf
within the nginx
directory.
This configuration sets up nginx to handle client requests by forwarding them to a Django application running on a different server (or container).
However, it's important to note that only NGINX will be accessible externally, while the Django App and database server will remain private.
Now, let’s run the application from the compose.yaml file and click on the play icon.
The Django Server, NGINX, and Postgres are now operational and running.
Let’s now check it in the browser.
Fantastic! The app is successfully up and running.
So, everything is working completely fine. Let’s check the Django Admin.
Unexpectedly, while the page is loading correctly, it seems that UI elements such as JavaScript and CSS have not been loaded.
The rationale for using NGINX alongside Gunicorn is that Gunicorn, being an application server, should not serve static files. Therefore, for managing both static or media files, NGINX is employed due to its proficiency in handling such content.
Static Files
Let’s now fix the staticfiles configuration.
Update STATIC_URL
and STATIC_ROOT
in settings.py
In Django, STATIC_URL
and STATIC_ROOT
are settings used to manage static files, which are typically CSS, JavaScript, and image files that are not dynamic and don't change per request.
Also, add a volume to the server and nginx services in compose.yaml
so that each container will share a directory named "staticfiles"
In the Dockerfile, we must create the "/app/staticfiles"
directory. As Docker Compose typically mounts named volumes with root permissions, using a non-root user leads to a permission denied error when executing the collectstatic
command if the directory isn't pre-existing.
Then, modify the NGINX configuration to direct requests for static files to the "staticfiles" folder.
The final last step is to update the CSRF_TRUSTED_ORIGINS
under settings.py
The CSRF_TRUSTED_ORIGINS
setting in Django is about ensuring that only safe and trusted web locations (origins) can send certain types of requests to your website.
Let’s retest the application again. Make sure to tear down the application before going ahead.
Run in through Terminal:
docker-compose down –--remove-orphans
Here's a simpler solution! Execute it via PyCharm.
Open compose.yaml
and re-run the play icon again.
Everything is operating perfectly well.
Let’s get inside the server container terminal and run the following commands:
migrations
&createsuperuser
collectstatic
You can see that migration, superuser and the static files were successfully executed.
Let’s head over to the browser and check again.
Amazing! The application is working fine and the UI has loaded up perfectly.