Follow:
Deploying Django Apps with Docker: A Step-by-Step Guide | Better Stack Community
A brief tutorial to configure a django project over docker (without docker compose)
Django, a powerful web framework for Python, has gained immense popularity for its ability to help develop robust web applications quickly. Once your Django application is ready for deployment, it's essential to have a seamless and efficient deployment process that guarantees consistency across different environments. This is where Docker, a versatile containerization platform, comes into play.
Docker offers a convenient way to package your Django application with its dependencies and configurations into a self-contained unit called a container. These containers are lightweight, portable, and isolated from the underlying system, making them ideal for deploying applications consistently across different environments.
By Dockerizing your application, you'll unlock several compelling benefits:
- Simplified dependency management that ensures consistent software and library versions across different environments, eliminating the "works on my machine" problem.
- Isolated environments that guarantee consistent behavior regardless of the underlying host system, making deployment and maintenance easier.
- Built-in scalability and management capabilities that empowers your application to handle increased traffic and workload demands by leveraging Docker's container orchestration tools, such as Docker Swarm or Kubernetes.
- Enhanced portability that enables you to develop and test your applications locally, and deploy them on any Docker-supported platform.
This tutorial will walk you through containerizing your Django application with
Docker. You'll start by cloning an existing Django application from a GitHub
repository that performs CRUD operations with a PostgreSQL database. Then you'll
create a Dockerfile
and build a Docker image locally to ensure it functions
correctly. You'll also learn to share the Django container image using a public
container registry like DockerHub.
Additionally, you will see the steps involved in deploying the application with Docker compose. By the end of this tutorial, you will be able to deploy your Django applications using Docker confidently.
Side note: Get a Python logs dashboard
Save hours of sifting through Python logs. Centralize with Better Stack and start visualizing your log data in minutes.
See the Python demo dashboard live.
Prerequisites
To ensure a smooth understanding and implementation of the tutorial, ensure that:
- You have a basic understanding and familiarity with Python and Django concepts.
- Python3 and Pip must be installed on your computer.
- You have basic command-line skills.
- PostgreSQL is installed and running on your machine.
- You've signed up for a free DockerHub and GitHub account.
After ensuring you've met the above prerequisites, you can proceed with the rest of this tutorial. Note that all the commands present in this article were tested on a fresh Ubuntu 22.04 machine.
Step 1 — Installing Docker locally
If you don't have Docker installed already, follow the relevant link below to get the Docker Engine up and running on your machine:
Once Docker is installed, we recommend that you configure it such that you don't
need to prefix the docker
command with sudo
. The official Docker
documentation provides detailed instructions on how to configure Docker without
sudo
access for
Linux and
macOS.
To verify that Docker is successfully installed and running on your machine, execute the commands below in the terminal:
Output
Docker version 24.0.5, build ced0996
sudo systemctl status docker
Output
● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled) Drop-In: /usr/lib/systemd/system/service.d └─10-timeout-abort.confActive: active (running) since Tue 2023-06-27 07:54:14 CAT; 3 days ago
TriggeredBy: ● docker.socket Docs: https://docs.docker.com Main PID: 23146 (dockerd) Tasks: 23 Memory: 92.0M CPU: 10min 15.078s CGroup: /system.slice/docker.service └─23146 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
The above output confirms that Docker is ready for use, so you can proceed to the next step where you'll create a PostgreSQL database for your Django application.
Step 2 — Setting up a local PostgreSQL database
In this section, you will create a database for the
Django todo application
that we'll be containerizing later. Assuming PostgreSQL is already installed on
your machine, execute the command below to access the psql
CLI with the
default postgres
user:
sudo -u postgres psql
In the psql
interface, create a new user/password combination as follows:
CREATE USER <username> WITH PASSWORD '<password>';
Output
Next, create a new database for the todo application and set the new user that you created as the owner:
CREATE DATABASE django_todo OWNER <username>;
Output
You can now quit the current session with \q
, then run the following command
to log in once again with your newly created user. Ensure to enter your password
if prompted:
psql -U <username> -d django_todo -W
If you're able to login to psql
successfully, you may proceed to the next step
where you'll set up the todo application on your computer.
Step 3 - Setting up the demo project
In this section, you will set up a simple Django todo application on your machine and run it locally so that you'll confirm that it works as expected before proceeding to Dockerize it.
Start by forking the demo project to your GitHub account, then clone the resulting repository to your computer through the command below:
git clone https://github.com/<username>/django-todo-app
Once the Django project is cloned successfully, navigate into the project
directory and view the top-level structure using the ls
command:
The project structure should appear as shown below:
Output
django_project manage.py requirements.txt todo_app venv
Here's a brief explanation of what each entry comprises:
django_project
: This is the main directory of the Django project. It contains the core files and configurations related to the project.todo_app
: This is the directory that contains the files for the todo application.venv
: This directory represents a Python virtual environment. It allows you to install project-specific dependencies and isolate them from the system-wide Python installation.manage.py
: This file is a command-line utility provided by Django. It allows you to perform various tasks related to the project, such as running the development server, applying database migrations, or executing custom management commands.requirements.txt
: This file contains the dependencies used in the project. It lists the names and versions of the Python packages or modules that the project depends on. Installing dependencies using this file ensures that the application environment is easily reproducible on different machines.
In summary, the Django project contains a todo application that performs basic
CRUD operations: create
, read
, update
, and delete
on a list of todo
items.
The next step is to create a .env
file at the root of your project and fill it
with your PostgreSQL credentials. Go ahead and the following code to the file
and ensure to replace the placeholders:
.env
DATABASE_NAME=django_todo DATABASE_USER=<postgresql_username> DATABASE_PASSWORD=<postgresql_password> DATABASE_HOST=localhost DATABASE_PORT=5432
Now, return to your terminal and activate the Python virtual environment with the following command:
source venv/bin/activate
This should change the prompt in your shell to include (venv)
.
You may now install the dependencies for the project by executing the following command:
pip3 install -r requirements.txt
If you encounter an error while building the psycopg2
, you may install the
following packages, then retry the command once again:
sudo apt install libpq-dev python3-dev
After installing the project dependencies, run the database migrations for the application with the following command:
python3 manage.py migrate
Output
Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, todo_app Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK Applying todo_app.0001_initial... OK
You are now set to launch the application server at this stage. Go ahead and run the command below to set it up:
python3 manage.py runserver
Output
Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). June 08, 2023 - 19:36:49 Django version 4.2.1, using settings 'django_project.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Navigate to http://localhost:8000/ in your preferred
web browser to view the todo_app
application in action:
To confirm that everything works, create a todo
item by clicking on the Add
Todo button at the top left corner of the homepage. You will be redirected to
the /create
route, where you can input the title
of your item, a
description
, a due date
, and check whether the item has been completed or
not.
Once you click the Save button, you should see the newly added item on the homepage:
Now, return to your psql
interface and execute the following sql
query
below:
SELECT * FROM todo_app_todo;
You should see your recently entered todo
item as follows, confirming that
everything is working as expected:
Output
id | title | description | due_date | completed ----+--------------+-----------------------------------------------------+------------+----------- 1 | Learn French | Download the Duolingo App and begin my French class | 2023-07-20 | f (1 row)
At this point, you may quit both the development server and the psql
interface
database by pressing CTRL-C
and typing \q
respectively in the terminal.
Now that you've successfully set up your Django application locally, let's move
on to the next step where you'll create a Dockerfile
for your project.
Step 4 — Creating a Dockerfile for your project
In this section, you will create a Dockerfile
that defines instructions for
building a Docker image for your project. You'll specify the base image, copy
the application code, install dependencies, configure the container environment,
and finally specify the command to launch the application server.
Create a Dockerfile
in the root of your project directory as follows:
Paste the following code into the file and save it:
django-todo-app/Dockerfile
FROM python:3.11 WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
Each line in the Dockerfile
above represents a specific instruction for
building the Docker image, and the instructions are executed from top to bottom.
Let's go through each line of the Dockerfile
to understand its purpose:
FROM python:3.11
: This line specifies the base image for our application's Docker image. In this case, it's the Python 3.11 image, which contains the essential OS files and dependencies for running Python applications. All subsequent instructions in this file will be committed on top of this base image.WORKDIR /app
: Sets the working directory inside the Docker container to/app
. This directory is where subsequent commands will be executed within the container. Note that this directory is created if it doesn't already exist.COPY requirements.txt .
: Copies therequirements.txt
file from the project directory on your machine to the current working directory inside the Docker container (/app
).RUN pip install --no-cache-dir -r requirements.txt
: This line installs all the dependencies listed inrequirements.txt
using thepip install
command. The--no-cache-dir
flag preventspip
from using its cache during the installation process.COPY . .
: Copies the entire project directory from the host machine to the/app
directory in the Docker image.EXPOSE 8000
: Informs Docker that the application will listen on port8000
. However, it doesn't publish the port to the host machine.CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
: Finally, this instruction specifies the default command to run when the Docker container is launched. In this case, the application server listens on all available network interfaces on port8000
.
The Dockerfile
above sets up a container environment for running your Django
application. It uses Python 3.11 as the base image, installs the project
dependencies, copies the application code into the image, exposes port 8000, and
specifies the command to run a container based on this Docker image is launched.
These instructions provide a way to run the Django application in an isolated
and reproducible environment using Docker.
Now that you've specified all the necessary instructions for building the Docker image, you can build the image in the next step.
Step 5 — Building the Docker image
In this section, you will use the Dockerfile
created in the previous section
to build the Docker image for your Django project. Before you run the
docker build
command to build the image, create a .dockerignore
file at the
root of your project:
Add the following line to the file:
django-todo-app/.dockerignore
The .dockerignore
file describes files and directories you want to exclude
when building a Docker image. It follows a roughly similar (but not identical)
syntax to .gitignore
files. You can
learn more about its syntax here.
The above .dockerignore
file ignores the .env
file that contains the
application secrets so that it's not copied into the Docker image. This is
important so your application secrets are not exposed in the Docker image. In a
subsequent section, we will discuss the proper way to pass a .env
file to the
Docker container. Now, go ahead and execute the command below to build the
image:
docker build -t django-todo-app .
Docker will use the current directory as the build context to create the image
based on the instructions in the Dockerfile
. The image is also tagged
django-todo-app
, so it's easy to identify amongst your other Docker images.
Once the image is built successfully, you should see the following output:
Output
[+] Building 245.1s (11/11) FINISHED ...
Go ahead and verify the existence of the Docker image by running the command below:
You should see the newly built image in the output:
Output
REPOSITORY TAG IMAGE ID CREATED SIZE django-todo-app latest a38a1effee6c 27 seconds ago 1.11GB
In the next step, we will turn our attention to building a Docker image that will run the PostgreSQL server.
Step 6 — Building a PostgreSQL image
In this section, you'll run a PostgreSQL container image and establish a network connection between the todo app container and the PostgreSQL container. This will ensure your Django application works properly on your local machine before deploying it to production.
First, deactivate your virtual environment and stop the local PostgreSQL server by typing the following commands:
sudo systemctl stop postgresql
Next, create a Docker network that will facilitate communication between the
Django application container and the PostgreSQL container using the command
below. It will create a network named my-django-postgres-network
to connect
the containers.
docker network create my-django-postgres-network
Output
c1f819a2b9868e25148167be8418875a4f2e8fd9192a2f668829fee565e2ed10
The next step is to build and run a Docker container for the PostgreSQL database using the official PostgreSQL image and connect it to the network you just created:
docker run --name my-postgres -p 5432:5432 -e POSTGRES_USER=<your_postgres_user> -e POSTGRES_PASSWORD=<your_posgres_user_password> --network my-django-postgres-network -d postgres
Replace the placeholder variables in the above command with the DATABASE_USER
and DATABASE_PASSWORD
variables used in your .env
file. This will create a
PostgreSQL container with a user with the specified username and password during
initialization.
Once the PostgreSQL container is created and running, you should see the following output:
Output
... 4ae692d11ad3: Pull complete Digest: sha256:31c9342603866f29206a06b77c8fed48b3c3f70d710a4be4e8216b134f92d0df Status: Downloaded newer image for postgres:latest d7289abba02eba999e9449125653c34936c7dd19337aa7a3901f23c0a8fbf52c
You can confirm that the PostgreSQL container is up through the command below:
The STATUS field should report "Up" followed by the uptime:
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a5c1ac476e54 postgres "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp my-postgres
The next step is to start an interactive bash session within the PostgreSQL container so that you can set up your database:
docker exec -it my-postgres bash
You should see a new bash prompt with the root
user like this:
Output
root@1ea83503fc9b:/#
Enter the following command to launch the psql
interface:
psql -U <database_user>
Next, create the database for your Django application using the same name as before:
CREATE DATABASE django_todo OWNER <database_user>;
Output
You may now exit the psql
interface by typing \q
, then exit the bash shell
by typing exit
. At this point, your PostgreSQL database has been set up
successfully within the container so you may proceed to the next section where
you'll run the Django application container and connect it to the PostgreSQL
container.
Step 7 — Running the Django application container
In this section, you'll start a new container for your Django application based
on the django-todo-app
image built in Step 5. Before you do that though,
update the DATABASE_HOST
value in your .env
file as follows:
django-todo-app/.env
. . . DATABASE_HOST=my-postgres . . .
This change allows your Django application container (once created) to connect
to the PostgreSQL container created in step 6 above. The value of
DATABASE_HOST
must correspond with the name of the PostgreSQL container, which
in this case is my-postgres
.
You're now all set to run the Docker container for your Django application. Execute the command below to start the container, specify its network, and link it to the PostgreSQL container:
docker --env-file .env run --name django-todo-app-c1 -p 8000:8000 --network my-django-postgres-network -d django-todo-app
This command will create a container named django-todo-app-c1
for your Django
application and map port 8000
from the container to port 8000
on your local
machine. Notice how the .env
file is fed into the container via the
--env-file
option so that the environmental variables are applied properly
before the application launches.
Once the container is running, you should see the following output:
Output
40b68935ef40f0c5a5e4206bd82ed4171ba9620d85edde7b91174b1b0c97ef2e
Confirm that both the PostgreSQL and your Django application containers are running by executing the following command:
If both containers are up and running, you should see the following output:
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bf68ef492145 django-todo-app "python manage.py ru…" 22 seconds ago Up 22 seconds 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp django-todo-app-c1 3ce92be1091c postgres "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp my-postgres
Next, apply the migrations once again using the following command:
docker exec -it django-todo-app-c1 python manage.py migrate
Output
Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, todo_app Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK Applying todo_app.0001_initial... OK
Now, visit the following address in your preferred web browser to view your
Django todo_app
application once again:
http://127.0.0.1:8000/
You should be able to access your Django application running inside the Docker container:
You can also try to add a new todo item to very that the connection to the PostgreSQL container also works perfectly:
Now, execute the following commands to stop and remove the running PostgreSQL and Django app containers since you have verified that the application works as expected:
docker rm my-postgres --force
docker rm django-todo-app-c1 --force
In the next section, you will learn how to share the Docker images you've just built so that others can download them and run them on their machines.
Step 8 — Sharing the Docker image with a different user or machine
In this step, you will explore sharing the Docker image with a different user or machine. Sharing the image allows others to access and deploy your application in their environments reproducibly. You'll also see how to push the image to a public Docker registry called Docker Hub, a cloud service for storing and distributing Docker images.
Start by logging into your Docker Hub account on your host machine with the following command, you can skip this process if you are already logged in:
This command will prompt you to input your Docker Hub username and password. Once you have inputted these values, you should see the following output if authenticated successfully:
Output
Tag the image with your Docker Hub username and repository name with the following command. Doing this will provide a standardized way of identifying and managing your images on Docker Hub.
docker tag django-todo-app <dockerhub_username>/django-todo-app
Next, execute the command below to push the image to your Docker Hub repository. It uploads the entire Docker image to your DockerHub account so that you can download it on other machines or make it accessible to other team members.
docker push <dockerhub_username>/django-todo-app
Output
... b4b4f5c5ff9f: Mounted from library/python b0df24a95c80: Mounted from library/python 974e52a24adf: Mounted from library/python latest: digest: sha256:ceb417f8f45e4ffe9724e052f0bc29d8f596dfdf923365b93288cb30b4ec5ef4 size: 2844
Once you head over to your Docker Hub repository, you should see the new entry for your Django application as follows:
At this point, you can download the image onto a different machine using the
docker pull
command as follows:
docker pull <dockerhub_username>/django-todo-app
This will download the Docker image accordingly so that you may run containers based on that image.
Step 9 - Automating the building and sharing process with GitHub actions
In this step, you will streamline the process of building and sharing the Docker image, using a continuous integration and continuous deployment (CI/CD) pipeline made with GitHub actions. This will ensure that whenever you push a commit to your repository, a Docker image will be automatically built and uploaded to Docker Hub without any further action on your part.
Ensure that you're at your project root before running the command below to
create the .github/workflows
directory:
mkdir -p .github/workflows
Next, create the following YAML file within that directory:
code .github/workflows/docker.yml
Paste the following code into the file, and ensure to replace the placeholders:
.github/workflows/docker.yml
name: Build and share Docker Image to Docker Hub on: push: branches: - main jobs: build-and-share: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v4 with: context: . push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO_NAME }}:latest
Notice that file above contains three secrets: DOCKERHUB_USERNAME
,
DOCKERHUB_REPO_NAME
, and DOCKERHUB_TOKEN
. The DOCKERHUB_TOKEN
is an access
token required to pushing the Docker image from GitHub to Docker Hub. You can
follow
this guide
to create the token on your Docker Hub account. Ensure to copy this token and
keep it in a safe place.
Next, set up secrets in your GitHub repository to store the values for your DockerHub credentials by following the steps:
- Go to your repository on GitHub.
- Click on the Settings tab.
- In the left sidebar, click on Secrets and Variables then Actions.
- Click on the New repository secret button.
- Provide a name for your secret, for example,
DOCKERHUB_USERNAME
. - Enter the value for the secret in the Secret field.
- Click the Add secret button to save the secret.
Repeat steps 4-7 for the DOCKERHUB_TOKEN
and DOCKERHUB_REPO_NAME
secrets.
Once you've saved the secrets to your repository, you can now then stage, commit and push your changes to GitHub:
git commit -m "add Dockerfile and configure github actions"
git push origin main
Navigate to the Actions
tab on your GitHub repository to view the status of
your workflow.
Considering the image above, you can see that the pipeline ran to completion. If not, wait a little for the pipeline to complete. Afterward, verify that it was successful by viewing your Docker Hub repository.
Step 10 — Deploying the Django application with Docker Compose
Docker Compose is a tool that simplifies the management of multi-container Docker applications. It allows you to orchestrate your Django application stack, including the web server, database, and other services.
Instead of manually creating and managing individual Docker containers using the
docker run
command, Compose lets you define and manage multi-container
applications in a single YAML file. This saves time and provides a structured
way to handle complex applications by specifying all the relevant services,
configurations, and dependencies.
In this section, you'll set up a Docker Compose configuration for your Django
application, and deploy your application accordingly. Start by creating a
docker-compose.yml
file in the root of your Django project, then paste in the
following contents:
code docker-compose.yml
django-todo-app/docker-compose.yml
version: '3.8' services: my-postgres: image: postgres:15 container_name: db environment: - POSTGRES_DB=${DATABASE_NAME} - POSTGRES_USER=${DATABASE_USER} - POSTGRES_PASSWORD=${DATABASE_PASSWORD} ports: - '5432:5432' volumes: - pg_data:/var/lib/postgresql/data web: build: . container_name: django ports: - '8000:8000' volumes: - .:/app environment: - DJANGO_ENV=${DJANGO_ENV} - DATABASE_NAME=${DATABASE_NAME} - DATABASE_USER=${DATABASE_USER} - DATABASE_PASSWORD=${DATABASE_PASSWORD} - DATABASE_HOST=${DATABASE_HOST} - DATABASE_PORT=${DATABASE_PORT} depends_on: - my-postgres volumes: pg_data:
The Docker compose configuration above defines two services: my-postgres
and
web
. It sets up a PostgreSQL database service (my-postgres
) and a Django web
application service (web
), then configures both services to communicate with
each other and provide the necessary environment and volume configurations for
development.
The next step is to build the images for the services defined in the Docker
Compose file using docker compose
. Before proceeding, ensure that the
Compose plugin is installed.
You can verify this by typing:
docker compose version
You should observe the following output:
Output
Docker Compose version v2.20.2
Next, run the command below to build the images defined in the
docker-compose.yml
file:
docker compose build
Output
my-postgres uses an image, skipping Building web [+] Building 75.8s (11/11) FINISHED ... => [5/5] COPY . . 2.7s => exporting to image 2.0s => => exporting layers 2.0s => => writing image sha256:b7174aee618ff55c470f30dd118b419527210e1ba18fc3ba42bc2eadf79cb1a8 0.0s => => naming to docker.io/library/django-todo-app_web
Next, stop and remove any existing containers on your machine using the commands below:
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
But if there are no running containers, you will see the error message:
Output
$(docker ps -a -q) "docker stop" requires at least 1 argument. See 'docker stop --help'. Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] Stop one or more running containers
Afterward, run the command below to create and launch the defined services.
Ensure that it is run in the same directory as your .env
file so that the
environmental variables specified in docker-compose.yml
are substituted
accordingly:
Output
Digest: sha256:362a63cb1e864195ea2bc29b5066bdb222bc9a4461bfaff2418f63a06e56bce0 Status: Downloaded newer image for postgres:15 Creating db ... done Creating django ... done Attaching to db, django db | db | PostgreSQL Database directory appears to contain a database; Skipping initialization db | db | 2023-07-09 09:28:53.430 UTC [1] LOG: starting PostgreSQL 15.3 (Debian 15.3-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit db | 2023-07-09 09:28:53.430 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 db | 2023-07-09 09:28:53.430 UTC [1] LOG: listening on IPv6 address "::", port 5432 db | 2023-07-09 09:28:53.503 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" db | 2023-07-09 09:28:53.556 UTC [29] LOG: database system was shut down at 2023-07-09 09:24:44 UTC db | 2023-07-09 09:28:53.575 UTC [1] LOG: database system is ready to accept connections django | Watching for file changes with StatReloader
From the output above, you can see that it creates and starts the containers
based on the built images, the sets up the network and volumes specified in the
docker-compose.yml
configuration file.
In a different terminal, apply database migrations for your Django application using the following command:
docker compose run web python manage.py migrate
[ouput] Creating django-todo-app-1_web_run ... done Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, todo_app Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK Applying todo_app.0001_initial... OK
To verify that the application works as expected, visit
http://localhost:8000/ in your browser and add a new
todo
item. It should list the new item once you hit the Save button as
before.
Return to your terminal, and create an interactive bash session for the Docker container running your PostgreSQL database with the following command:
docker compose exec my-postgres psql -U <your-postgres-user> -d django_todo
Output
psql (15.3 (Debian 15.3-1.pgdg120+1)) Type "help" for help. django_todo=#
In the psql
interface, use the following query to verify that your todo
item
exists in the database:
TABLE todo_app_todo;
Output
id | title | description | due_date | completed ----+---------------+---------------------------------------------+------------+----------- 1 | Buy black tea | I will buy black tea tomorrow at the market | 2023-07-12 | f (1 row)
With the output above, you've successfully deployed your Django project with Docker Compose!
Final thoughts and next steps
In this article, you learned how to simplify the deployment process for Django applications by containerizing them with Docker and Docker Compose. By encapsulating your Django application, along with its dependencies and runtime environment, within a Docker container, you can achieve consistent deployments across different environments and avoid the common "it works on my machine" problem.
You also discovered how to automate the building and sharing of Docker images through Docker Hub and GitHub Actions. This allows you to easily store and distribute your Docker images, making it convenient for deployment on different machines or sharing with others.
As you continue your journey with Docker and Django, here are some suggested next steps:
Explore more advanced Docker concepts, such as multi-stage builds, Docker networking, and Docker Swarm for scaling and managing containers in a cluster.
Look for ways to optimize Docker builds by reducing image size, utilizing the build cache, and squashing image layers.
Learn how to manage your application logs in Docker and transport them to other destinations.
Familiarize yourself with Docker security best practices and the most common Python errors to ensure the security of your containerized Django application.
The complete code used in this article can be found in this GitHub repository. Thanks for reading, and happy coding!