This article is a brief review of the steps to set up a local Apache HTTP server with Django suitable for local development and testing. We don't address security concerns in this article. Before deploying a web server on a public interface, security topics should be thoroughly reviewed, and these can be found on both the Apache and Django project web sites (see links below).
In case you're not familiar with the open source technologies discussed in this article, a brief introduction of each component is provided below:
- Ubuntu Linux 18.04 is a recent long-term release for the very popular and stable Linux desktop distribution. This operating system can be downloaded for free at https://www.ubuntu.com and will run on most PCs without too much trouble. Ubuntu 20.04 has also been released this year and is certainly worthy of your consideration.
- The Apache HTTP Server Project is a popular & powerful Web server for serving up dynamic content across the Web. It may be overkill for personal network installations; however, it can be very useful for developing & testing new technologies locally before deploying to a server (e.g., bare metal) in the cloud.
- Django is a Web framework for building world-class web sites. The framework uses Python, which is a very friendly & powerful scripting language used in an endless number of applications on local networks and data servers.
- WSGI (Web Server Gateway Interface) is a connector module that enables communication between the Apache web server and the Django web framework. The default data flow is one where a Web request via a browser is routed to the Apache web server. The server in turn makes a request call into Django via WSGI, which is an Apache module. Django processes the request and returns the response to Apache for transmission back across the Internet to the browser.
We're starting with a clean x86 64-bit Ubuntu 18.04 system with Python 3.6 but with neither Apache nor Django installed:
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.5 LTS Release: 18.04 Codename: bionic
note: lsb_release provides "certain LSB (Linux Standard Base) and distribution-specific information".
$ which apache2 $ dpkg -L apache2 dpkg-query: package 'apache2' is not installed ... $ python3 Python 3.6.9 (default, Oct 8 2020, 12:12:24) ... >>> import django ... ModuleNotFoundError: No module named 'django'
Use Ubuntu's apt (Advanced Packaging Tool) to install our base apache2 installation
$ sudo apt install apache2 Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.2-0 Suggested packages: www-browser apache2-doc apache2-suexec-pristine | apache2-suexec-custom The following NEW packages will be installed: apache2 apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.2-0 0 upgraded, 9 newly installed, 0 to remove and 0 not upgraded. ... Enabling module mpm_event. Enabling module authz_core. Enabling module authz_host. Enabling module authn_core. Enabling module auth_basic. Enabling module access_compat. Enabling module authn_file. Enabling module authz_user. Enabling module alias. Enabling module dir. Enabling module autoindex. Enabling module env. Enabling module mime. Enabling module negotiation. Enabling module setenvif. Enabling module filter. Enabling module deflate. Enabling module status. Enabling module reqtimeout. Enabling conf charset. Enabling conf localized-error-pages. Enabling conf other-vhosts-access-log. Enabling conf security. Enabling conf serve-cgi-bin. Enabling site 000-default. ...
We want mod-wsgi, which is an Apache module for the implmenentation of python WSGI. This will enable us to run our Django application using Apache and also do cool things like SSE server push via WSGI.
Install mod-wsgi for python 3 making sure to use the Apache library for python3. Information on various Ubuntu HTTP server packages can be found on the Ubuntu package site.
$ sudo apt install libapache2-mod-wsgi-py3 Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: libapache2-mod-wsgi-py3 0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. ... apache2_invoke: Enable module wsgi
Note the last line above that states the module was enabled. Although it's not necessary, let's check that it is indeed installed and where it's located:
$ dpkg -S mod_wsgi libapache2-mod-wsgi-py3: /usr/lib/apache2/modules/mod_wsgi.so libapache2-mod-wsgi-py3: /usr/lib/apache2/modules/mod_wsgi.so-3.6
Check if our Apache Web Server is running
$ ps -e | grep apache 3231 ? 00:00:00 apache2 3234 ? 00:00:00 apache2 3235 ? 00:00:00 apache2
You should now be able to visit your Linux PC via a browser and see the Apache2 Ubuntu Default Page.
Install Django from source
It's relatively easy to work with Django from source using the Git version control system. We'll install it under our /build tree:
$ mkdir -p /build; cd /build $ git clone https://github.com/django/django.git Cloning into 'django'...
Checkout the latest production branch (we could also checkout stable/2.2.x)
$ cd django $ git tag -l | grep ^3.1 3.1 3.1.1 3.1.2 3.1.3 3.1a1 3.1b1 3.1rc1 $ git checkout 3.1.3 Note: checking out '3.1.3'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b <new-branch-name> HEAD is now at 4264d78f2d [3.1.x] Bumped version for 3.1.3 release.
Create our own development branch so we can make changes to the source, commit them, create patches, etc.
$ git checkout -b dev3.1.3 Switched to a new branch 'dev3.1.3'
Determine where to configure the django library:
$ python3 -c "import site; print(site.getsitepackages())" ['/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages'] $ cd /usr/local/lib/python3.6/dist-packages $ sudo sh -c "echo '/build/django' > django.pth"
Note that the last line above creates a path configuration file so python can find our Django library.
Verify it's installed in our python3 environment:
$ python3 -c 'import django; print(django.VERSION)' (3, 1, 3, 'final', 0)
Now we have Apache running and Django installed so python3 can import it and use it.
Create an example Django test project: tstdj under /build
Before trying to create a new Django project, we may need to take care of some dependencies (i.e., pytz and asgiref). There are multiple ways to do this, and one is shown below:
$ sudo apt install pip3 $ sudo -H pip3 install pytz asgiref sqlparse
Now let's create a Django project:
$ cd /build $ python3 /build/django/django/bin/django-admin.py startproject tstdj
Find your settings.py file under tstdj and modify your ALLOWED_HOSTS so it includes your interface (e.g., "192.168.1.17"). If you don't do this, you'll see an error when you visit your website explaining what you need to fix (courtesy of the Django developers).
If you want to take a break and verify your Django test project is OK, you can use:
$ cd /build/tstdj $ python3 manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK ... $ python3 manage.py runserver Performing system checks... System check identified no issues (0 silenced). ...
If you are unfamiliar with runserver and database migrations, head over to djangoproject.com and walk through their very good tutorial.
Configure the Apache server to serve Django
Now it's time to tie it all together and serve up your Django project using Apache and mod-wsgi.
Open two terminals side-by-side to modify Apache conf files and view Apache logs:
$ cd /etc/apache2 $ grep APACHE_LOG_DIR envvars export APACHE_LOG_DIR=/var/log/apache2$SUFFIX
Second terminal, making use of the path determined for APACHE_LOG_DIR above:
$ cd /var/log/apache2 $ ls access.log error.log other_vhosts_access.log
This second terminal will let you view errors in error.log and accesses in access.log.
For completeness sake, make sure mod-wsgi is enabled:
$ sudo a2enmod wsgi Module wsgi already enabled
In the first terminal open /etc/apache2/apache2.conf with your favorite editor after making a copy of it:
$ sudo cp apache2.conf apache2.conf.orig
Add the following lines to the end of apache2.conf:
WSGIDaemonProcess www-data processes=2 threads=12 python-path=/build/tstdj WSGIProcessGroup www-data WSGIRestrictEmbedded On WSGILazyInitialization On WSGIScriptAlias / /build/tstdj/tstdj/wsgi.py <Directory /build/tstdj/tstdj> Require all granted </Directory>
Note that there are many options for configuring, tuning, and tweaking both Apache and mod-wsgi, but these are similar to the ones we use on a production site. Refer to the documentation of both projects for further information.
Now, restart Apache and give it a try. If you have errors, review error.log in /var/log/apache2 or submit a comment below and we'll try our best to help you.
sudo apachectl -k restart
Lastly, make sure it's running:
$ ps -e | grep apache 13172 ? 00:00:00 apache2 13173 ? 00:00:00 apache2 13174 ? 00:00:00 apache2 13175 ? 00:00:00 apache2 13176 ? 00:00:00 apache2
In order to try your installation, open your browser and type in the IP address you set in your Django settings.py file for ALLOWED_HOSTS above. Hopefully, you see something similar to what is shown below.
- mod_wsgi Quick Configuration Guide