Sane Django Development with Docker

🖊️ 🔖 code 💬 9

Recently I started a new Django project, and this time I decided to go all in on Docker. No virtualenvs, no local databases - containers all the way.

There are about a million and ten articles about how to dockerize webapps by now. However, none of them seem to address one simple fact: we don’t simply want to dockerize our applications, we want to develop them too!

sane-django-docker contains a sample django project webapp as well as the necessary config files to run both a development and production server.

Checkout and Go®

One of my goals was to make the codebase development first. One should be able to checkout the codebase and run at most two or three commands to have a real development environment set up. This means we should have a live-reloading Django development server, debug mode on, and a postgresql database. I also can’t stand logic in my settings.py files, so it is left as vanilla as possible. It will import a local_settings.py file at the end, but besides that it is 100% constants. No os.getenv() to be found.

To start the development server simply run:

docker-compose up

Django will complain about the postgres database not existing so we’ll create one:

docker exec sanedjangodocker_db_1 createdb -Upostgres webapp

Sweet Jane! We now have a Django development server running at http://localhost:8000 along with a postgresql database! Make a code change and watch it reload. This is how code was meant to be written.

So what’s the secret sauce? A super simple Dockerfile and an equally simple docker-compose.yml file.

Deployment ain’t that much harder

So getitng a dev environment up and running is all well and good, but we are going to have to deploy our code at some point. Deployment takes a few additional steps, but then again deployment probably should.

Let’s take a look at what we have:

.
├── deploy
│   ├── docker-compose.yml
│   ├── local_settings.py
│   ├── nginx-app.conf
│   ├── supervisor-app.conf
│   ├── uwsgi.ini
│   └── uwsgi_params
├── docker-compose.yml
├── Dockerfile
├── Dockerfile.prod
├── manage.py
├── README.md
├── requirements.txt
└── webapp
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

The deploy/ directory contains all our server configuration files. The directory also includes our local_settings.py which contains our production config. It is included in .gitignore and should not be included in source control!

Dockerfile.prod is our production dockerfile. It is based on Python:3.5, installs nginx, uwsgi and supervisord, copies our config files and finally runs manage.py collectstatic.

Let’s build an image from it:

docker build -f Dockerfile.prod -t webapp:latest .

That’s it! our production image is ready to go. To test it out locally first, we can run it:

cd deploy/ && docker-compose up

This should start our project in production mode, using the image we just built. Again, we need to initially create the database, and we should probably run migrations too:

docker exec sanedjangodocker_db_1 createdb -Upostgres webapp
docker exec sanedjangodocker_web_1 python3 manage.py migrate

Navigate to localhost:8700 and see your production-ready application being served!

Where to go from here

There are probably a few things you want to tweak for a real project such as the postgresql data volume in deploy/docker-compose.yml, and your ALLOWED_HOSTS setting in local_settings.py.

Of course, the nginx, uwsgi and supervisord config files are pretty basic, and probably should be scrutinized before a real life deploy.

Conclusions

All in all, I’ve found this to be a pretty frictionless workflow. The one annoyance I have is that both dockerfiles have to be in the top level directory, due to how Docker sends the build context to the server. Besides that there isn’t much to complain about - I’ll probably use this as a base for my future projects.


Wilson Duarte
Congratulations for the god job.
anonymous
Hello, I cannot access to the Django app at http://localhost:8700 (Error 404 from nginx) but I found that the URL http://localhost:8000 works and displays the Django default welcome page ("It worked !". Is that because of the file "uwsgi.ini" that contains "http = :8000" ? How can I access to the webapp ?
Daniel van Flymen
As an aside, how do you properly prevent against CSRF? ALLOWED_HOSTS is only going to see the hostname of the Docker container since nginx is running in the same container - so all IPs are going to be from the container?
Daniel van Flymen
Interesting setup... are you running nginx on the host? How do you get around the environment variable problem: I have a bunch of environment variables that need to be accessed by [settings.py](http://settings.py), I'd prefer to keep these in ephemeral memory on the host as writing them directly into the Dockerfile voids the point of using them.
Fingel  in response to Pascal van Kooten
If the container is not running, start it. Please read the docker documentation: [https://docs.docker.com/ref...](https://docs.docker.com/reference/commandline/start/)
Pascal van Kooten  in response to Pascal van Kooten
Did you find a solution?
Pascal van Kooten  in response to Fingel
Oh my bad, yea I tried that, but then it hands me back: Error response from daemon: Container sanedjangodocker_db_1 is not running
Fingel  in response to Pascal van Kooten
Yes. As I mentioned in the post: docker exec sanedjangodocker_db_1 createdb -Upostgres webapp
Pascal van Kooten
It sounds very promising, but whenever running `docker-compose` up I get the following error: db_1 | LOG: database system was shut down at 2015-10-11 18:56:57 UTC db_1 | LOG: MultiXact member wraparound protections are now enabled db_1 | LOG: database system is ready to accept connections db_1 | LOG: autovacuum launcher started db_1 | FATAL: database "webapp" does not exist Not really sure how to continue, any clue?