Docker-compose-ing a Django App - for complete starters
Tags: beginners, tutorial, docker, python
Published: 2022-01-31 14:08:18

In the earlier post, in which I described about [writing a simple Dockerfile for a Django App.](https://dev.to/mahyess/dockerizing-a-django-app-for-starters-3a5b) 

We will pickup where we left off earlier. If you are completely new to Docker, I recommend checking [the previous post](https://dev.to/mahyess/dockerizing-a-django-app-for-starters-3a5b) first.

The starter code that we are using for this post is [available in GitHub](https://github.com/mahyess/django-dockered/tree/1-base-docker) 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](https://docs.docker.com/compose/compose-file/).

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 `Dockerfile`s 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. `environment`s are preferred when the variables are public, whereas `env_file`s 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.
![Django Starting Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53vgda3gdpj40ozcqh65.png)

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](https://github.com/mahyess/django-dockered/tree/2-base-docker-compose)

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.