This article will show you how to run Django in production with docker and apache2/mod_wsgi. While there are several guides on how to do it, I found no simple enough tutorial on how to do it since all of the existing solutions require a lot of configuration or a custom docker image. The solution I found is way quicker and requires close to no configuration.
Guides I found:
So, let's do this. If you just want to see the final Dockerfile, scroll to the end of the article :)
I assume you have some Dockerfile with Django application setup and run Django via the command that spins up the development server:
FROM python:3.6
WORKDIR /code/
# copy and install requirements first to leverage caching
COPY requirements.txt /code/
RUN pip install -r requirements.txt
# copy the actual code
COPY . /code/
CMD ./manage.py runserver 0.0.0.0:8000
We basically need a working Apache setup to run python code via mod_wsgi. So in our Dockerfile, we need to install apache2 and apache2-dev.
RUN apt-get install apache2 apache2-dev
We want to run our project within a container. The mod_wsgi documentation says the easiest and preferred using docker is to use mod_wsgi-express as it does not require any configuration. mod_wsgi-express can be used as a command after you have installed it via pip. So we need to add:
RUN pip install mod_wsgi
Lastly, we simply have to run mod_wsgi-express which will start an apache instance with our Django project. This can be achieved via:
mod_wsgi-express start-server /code/project_name/wsgi.py --user www-data --group www-data
The wsgi.py file is auto-generated by Django's startproject
command and inside you app's folder,
i.e. django-project/project_name
.
So, to run our project within Apache after we've set-up everything in the Dockerfile,
we have to add a CMD
command at the end of the Dockerfile:
CMD mod_wsgi-express start-server /code/connect_web/wsgi.py --user www-data --group www-data
The --user
and --group
parameters make sure Apache isn't run as root which results in errors.
If you want to find out, just run the command without them.
Your final Dockerfile should now look like this:
FROM python:3.6
# update packages
RUN apt-get -qq update
RUN apt-get install --yes apache2 apache2-dev
RUN pip install mod_wsgi
RUN mkdir /code
WORKDIR /code
COPY . /code/
CMD mod_wsgi-express start-server /code/project_name/wsgi.py --user www-data --group www-data
If you have any problems, check the logs. Their locations are outputted when starting the container:
web_1 | Server URL : http://localhost:8000/
web_1 | Server Root : /tmp/mod_wsgi-localhost:8000:0
web_1 | Server Conf : /tmp/mod_wsgi-localhost:8000:0/httpd.conf
web_1 | Error Log File : /tmp/mod_wsgi-localhost:8000:0/error_log (warn)
web_1 | Request Capacity : 5 (1 process * 5 threads)
web_1 | Request Timeout : 60 (seconds)
web_1 | Startup Timeout : 15 (seconds)
web_1 | Queue Backlog : 100 (connections)
web_1 | Queue Timeout : 45 (seconds)
web_1 | Server Capacity : 20 (event/worker), 20 (prefork)
web_1 | Server Backlog : 500 (connections)
web_1 | Locale Setting : en_US.UTF-8
If you want to monitor them, just tail -f
them:
tail -f /tmp/mod_wsgi-localhost:8000:0/error_log
At first, I got an Internal Server Error
when opening the page on my machine.
After checking the logs as described above, mod_wsgi seemed to have problems with file permissions on .logs/debug.log
.
A simple chown www-data /code/.logs/debug.log
from within the container
which makes www-data the owner of the file solved it for me.