In the earlier post, in which I described about writing a simple Dockerfile for a Django App.
We will pickup where we left off earlier. If you are completely new to Docker, I recommend checking the previous post first.
The starter code that we are using for this post is available in GitHub if you want to follow along.
Let's see how the file structure looks before we start:
├── Dockerfile
├── manage.py
├── README.md
├── requirements.txt
└── main
├── asgi.py
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
If you are following from the previous post, you will notice I have renamed the 'main' project folder from todo to main. It's because I want to scale this tutorial series to use as a starting template for future projects. So I felt it's important to keep the folders' name as generic as they can be.
The folder structure shouldn't seem to be too confusing as they are not complicated to understand.
Let's go forward. First of all, create a directory in project root called app and move all existing contents to the app folder, so the folder structure now looks like:
.
├── app
│ ├── Dockerfile
│ ├── entrypoint.sh
│ ├── main
│ │ ├── asgi.py
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── manage.py
│ └── requirements.txt
└── README.md
3 directories, 12 files
Why do this? It's because we are incorporating docker-compose which by its nature, allows us to manage multiple tools for our project, django being one of them. Each of those tools and their configs should go to their respective directory for clear code and readability.
Now, let's create a docker-compose.yml file at the root directory, which now contains the app directory and write the following lines of code.
version: "3.8"
services:
app:
build:
context: ./app
dockerfile: Dockerfile
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./app:/app
ports:
- "8000:8000"
env_file:
- ./app/.env
environment:
- DEBUG=1
Let's go through the code and see what's happening
1.
version: "3.8"
We want to use docker-compose version 3.8. How does this versioning matter? That's a lot of complex topics for this post, but if you want to know more, you can check official docker website.
2.
services:
We want to list the 'tools' as mentioned earlier as array items within the services.
3.
app:
Each of the 'tools' needs their own name as recognition. app is just a generic name for the django container. You can put anything instead of app like django, server or even labrador.
4.
build:
context: ./app
dockerfile: Dockerfile
As we know, each 'tool' or container in docker languages, needs to build before we run. We learnt that in the previous post as well. docker-compose needs to know which folder to look for to build the container app. So, naturally, we put all the files related to the app container into app folder and gave the context name to ./app, as in app folder from where docker-compose.yml file is located.
At the same time, it also asks for Dockerfile location. So the name and location, relative to the folder provided in context. We may want to have multiple Dockerfiles later on, so it is convenient to have them in easy-to-remember place
5.
command: python manage.py runserver 0.0.0.0:8000
If you understand Django, this is just a standard run command which we want to do when we start the server later on.
6.
volumes:
- ./app:/app
The volumes mapping allows us to map local folders to folders inside the container. If you look into Dockerfile, we created a folder named app. The folder inside the container and local system stay synchronized with this command. It also triggers hot reloading of server whenever we change something in local system, so it's very important to keep it on while debugging. Else, we will have to restart the server on every code change.
7.
ports:
- "8000:8000"
Just like the previous post, we want to map our local port 8000 to container's port 8000 to access the server.
With this, you can remove the lines 7. EXPOSE 8000 and 8. CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] Since, both of them are now managed by docker-compose herein.
8.
env_file:
- ./app/.env
environment:
- DEBUG=1
Both env_file and environment can be used to set environment variables to the container. environments are preferred when the variables are public, whereas env_files which are usually git-ignored, can be used to set private environment variables like secret-keys, tokens and even passwords. We will look into how to use them later on.
For now, lets go to the terminal and hit up
docker-compose build
If everything went fine, you may now see Successfully built in the terminal.
So, we may now fire up the server by running
docker-compose up
We should now be presented with this beautiful screen when we navigate to localhost:8000 in our browser.

That's all it takes to run our django project with docker-compose. As simple as it can get.
All the code is available in GitHub
Up next, we will add postgres database and nginx reverse proxy to the setup and find-out how docker-compose systems can be scaled up.
Top comments (1)
Concise and to the point article. Thanks. When are you planning to write next parts of this series for postgres and nginx?