Dockerizing a Django App - for complete starters
Tags: django, docker, beginners, tutorial
Published: 2022-01-19 12:21:32

As starters, the term docker can be quite daunting and may seem a big cliff to overcome but it shouldn't be as such.

The vast amounts of tutorials covering a lot of information at once, doesn't help the starters, speaking from personal experience. That's why, this post is a single step towards the complete new world of docker. So, let's begin.

First thing first, you need to have a existing Django project that you are willing to Dockerize.

I have created a new Django project for this very purpose and going to use it as a starting template. Let's see files that are here inside this folder.

```
├── db.sqlite3
├── manage.py
├── requirements.txt
└── todo
    ├── asgi.py
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py
```
As a complete boilerplate, when run the server with `python manage.py runserver`, we are presented with our starting page.
![Django Starting Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53vgda3gdpj40ozcqh65.png)

So far, so good.

Let's create a file named Dockerfile in the root directory. The files tree needs to look like 

```
├── db.sqlite3
├── Dockerfile
├── manage.py
├── requirements.txt
└── todo
    ├── asgi.py
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py
```
Now, let's quote what Dockerfile is:

> A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

This is where we put the commands to build our image.

```
FROM python:3.10

RUN mkdir /app

COPY requirements.txt /app/

WORKDIR /app

RUN pip install -r requirements.txt

COPY . /app/

EXPOSE 8000

CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
```

Let's go through these commands one by one.

1. 
```
FROM python:3.10-slim
```
We are using `python:3.10-slim` as our base image from which we build our image. We can use `python:3.10` but it will generate larger build file, which we should be trying to reduce. Also, we can use `python:3.10-alpine` which reduces the size even further, but this will require extra care from our end. That's a topic for some other time. If you are interested in knowing further, you can go through [here](https://hub.docker.com/_/alpine).

2. 
```
RUN mkdir /app
```
It is pretty straight-forward. It creates a new directory called `app` in root directory of the image.

3.
```
COPY requirements.txt /app/
```
It copies the `requirements.txt` file from our project to the directory we just created. If you don't have this file yet, you can create one by entering the command `pip freeze > requirements.txt` which gets all packages that are currently installed in our django project and writes them into the said file.

4.
```
WORKDIR /app
```
It sets the current working directory to the directory we created. In other words, we `cd`ed to this directory. Any commands we enter from herein are triggered from this directory.

5.
```
RUN pip install -r requirements.txt
```
The image we used from `line 1`, `python:3.10-slim` as pip installed into it by default. So we can run any `pip` commands in the image. This command installs all the packages that we had installed in our local system previously.

6.
```
COPY . /app/
```
Similar to `line 3`, but now we copy all files and folders in the local system into the image. Now, why didn't we did this in `line 3` itself? If you wondered, that's good. It's because `docker` caches all the commands while building the image, so if we haven't changes `requirements.txt` further down the line in future, all commands from `1-5` aren't triggered again. Else, even if there aren't any changes in packages, but have changes in our project, `docker` will unnecessarily installs all the packages again.

7.
```
EXPOSE 8000
```
We are almost done. It `exposes` the port 8000 to "outside world" from image. Since we are going to run Django in port 8000, this is a gateway to the server we are going to run later.

8.
```
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
```
Finally, it runs the command `python manage.py runserver 0.0.0.0:8000` inside the image. That means, our server will now be up and running.

We have completed writing a very basic `Dockerfile` that works. Now, head up to the terminal and enter the command:

```
docker build -t todo/latest .
```
Let's wait for some time as docker builds our image. If you are wondering what's `todo/latest`, its a tag name. You can put anything as you want or even skip it to just run `docker build .`, but we will have to search for our image by going to through a list of images built in the system by going through `docker ps`. Let's not go out of our way for now. If you are interested, you can certain try it by yourself.

So, after waiting a couple of minutes, or seconds, depending on several factors, to complete the build, let's trigger the final command.

```
docker run -p 8000:8000 todo/latest
``` 
This command runs the `CMD` that we have provided in the Dockerfile. As for `8000:8000`, we are binding our local port `8000` to the image's port `8000`. If you wish, you can put `80:8000`, so it binds to local port `80` to the image's port.

If everything goes well, your terminal should get a normal Django server log. For me, it's
```
╰─ docker run -p 8000:8000 todo/latest
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
January 19, 2022 - 11:55:15
Django version 4.0.1, using settings 'todo.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
```

Let's check our browser for confirmation. We should be presented again with this beautiful page.

![Django Starting Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53vgda3gdpj40ozcqh65.png)

Congratulations, you have successfully "Dockerize" your Django application. 

As for the migrations warnings, you can add a line `RUN python manage.py migrate` after `line 6` and before `line 8`, and build again.

This is a very basic Dockerfile as mentioned. I want it to be understood by a complete beginner so haven't included a number of "gotcha"s to the Dockerfile, they can be learnt as we go and code. 

The complete code can be found in my [GitHub](https://github.com/mahyess/django-dockered). Feel free to check it out, if you are having any problems.