Tobias Hinz
10 April 2019
Reading time: 6 minutes
In this short guide we will describe how to set up an Apache server on Ubuntu 18.04 in order to serve multiple Django websites which use (potentially) different virtualenv environments1. Specifically, this tutorial shows you how to:
In the end, you will have a system from which you can serve multiple websites in parallel, in the form
www.myserver.com/website1
www.myserver.com/website2
...
where, if necessary, all websites can use different virtualenvs. This is especially useful if your websites use different versions of Django or other packages which are not compatible between each other.
We use the following OS and software versions, but later versions should (hopefully) also work:
We assume you have a running Ubuntu system on which you have sudo rights. The first step is to install the Apache server:
sudo apt-get update
sudo apt-get install apache2
During the installation, Apache registers itself with UFW to provide a few application profiles that can be used to enable or disable access to Apache through the firewall. List the ufw application profiles by typing:
sudo ufw app list
The available applications should be:
Apache: This profile opens only port 80 (normal, unencrypted web traffic)
Apache Full: This profile opens both port 80 (normal, unencrypted web traffic) and port 443 (TLS/SSL encrypted traffic)
Apache Secure: This profile opens only port 443 (TLS/SSL encrypted traffic)
OpenSSH: OpenSSH is a free implementation of the Secure Shell protocol.
Since we haven’t configured SSL for our server yet, we will only need to allow traffic on port 80:
sudo ufw allow 'Apache'
Now check that Apache is running:
sudo systemctl status apache2
In your output you should have information about the status of the Apache server:
Active: active (running) since Tue 2019-04-09 16:34:23 CEST; 18h ago
As a next step we’ll install Apache-dev, which is needed for mod_wsgi:
sudo apt-get install apache2-dev
In order to prevent any errors in your running system later check the settings in
sudo nano /etc/apache2/envvars
For example, we had to add
export LANG='en_US.UTF-8'
export LC_ALL='en_US.UTF-8'
in order to have the correct encoding in our websites. If you change anything with the language settings, make sure you have the necessary locales installed on the OS:
locale -a
Now that we have the Apache server set up and running we will continue with the mod_wsgi package.
Remember that we use Python 3, so consider setting an alias in your .bashrc
file to automatically use Python 3:
alias python=python3
First, we’ll install python-dev (check current version), distutils, and pip:
sudo apt-get install python3.6-dev
sudo apt install python3-distutils
sudo apt-get install python3-pip
Now we are ready to install mod_wsgi for Python 3:
sudo apt-get install libapache2-mod-wsgi-py3
Finally, install the virtualenv environment:
pip3 install virtualenv
Now that we have installed the needed software packages we are ready to set up our environment for the usage of Django. First we create a folder from which we will later on host all our various websites:
mkdir django_websites
Within this folder we also install our virtualenv, but you can also put your virtualenv(s) at any other location if you prefer:
cd django_websites
virtualenv django-virtualenv
Now activate the virtualenv and install the python packages you need for your first Django website that you want to host, e.g.
source django-virtualenv/bin/activate
pip install django
pip install numpy
...
Now copy the folder containing your Django website into the current folder:
cp -r django_website_1 django_websites
This folder should contain your database, your various apps, the manage.py
file, and the folder containing the settings.py
file. We will refer to the folder containing the settings.py
file as core
in this example:
django_website_1
middleware.py
db.sqlite
core
settings.py
...
app1
app2
...
As a first step we need to adapt the wsgi.py
file in your Django app so that all the paths point to the location of your virtualenv.
nano /home/django_websites/django_website_1/core/wsgi.py
The final wsgi.py
file should look something like this:
import os
import sys
import site
from django.core.wsgi import get_wsgi_application
# Add the site-packages of the chosen virtualenv to work with
site.addsitedir('/home/django_websites/django-virtualenv/lib/python3.6/site-packages')
# Add the app's directory to the PYTHONPATH
sys.path.append('/home/django_websites/django_website_1')
sys.path.append('/home/django_websites/django_website_1/core')
#to set enviroment settings for Django apps
os.environ['DJANGO_SETTINGS_MODULE'] = 'core.settings'
# Activate your virtual env
activate_env=os.path.expanduser('/home/django_websites/django-virtualenv/bin/activate_this.py')
exec(open(activate_env).read(), {'__file__': activate_env})
application = get_wsgi_application()
In the next step we will adapt the Apache server’s config file (e.g. /etc/apache2/sites-available/000-default.conf
).
sudo nano /etc/apache2/sites-available/000-default.conf
Below the already existing configuration, but before the </VirtualHost>
tag you can update the settings regarding your website. We add the following lines to point to the correct media and static folders (adapt to the location of your media and static folders, if you have any):
Alias /media/ /home/django_websites/django_website_1/media/
Alias /django_website_1/static/ /home/django_websites/django_website_1/static/
We also need to give Apache and mod_wsgi access to the required folders and files:
<Directory /home/django_websites/django_website_1/static>
Require all granted
</Directory>
<Directory /home/django_websites/django_website_1/media>
Require all granted
</Directory>
<Directory /home/django_websites/django_website_1/core>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
Finally, we need to make sure that Apache knows the link to the virtualenv. Below the previously added text we add the following lines, pointing it to the location of our virtualenv:
WSGIDaemonProcess django_website_1 python-path=/home/django_websites/django_website_1:/home/django_websites/django-virtualenv/lib/python3.6/site-packages
WSGIProcessGroup django_website_1
WSGIScriptAlias /django_website_1 /home/django_websites/django_website_1/core/wsgi.py
The first line spawns a WSGI daemon process called django_website_1
that is responsible for serving the Django application. The daemon name can essentially be anything but it is good practice to use descriptive names such as application names here. Since we are using a virtual environment we need to specify the alternate Python path so that mod_wsgi will know where to look for Python packages. The path must contain two directories, delimited by a colon: the directory of the Django project itself (/home/django_websites/django_website_1
) and the directory of the Python packages inside our virtual environment for that project (/home/django_websites/django-virtualenv/lib/python3.6/site-packages
). The second line tells that particular virtual host to use the WSGI daemon created beforehand and, as such, the daemon name must match between those two. The third line tells Apache and mod_wsgi where to find the WSGI configuration we modified in the previous step.
Finally, assign the correct access rights (choose which access rights are the ones you need specifically) to your website(s) (specifically the database and the media/static folders) and restart the Apache server:
sudo chmod -R 766 /home/django_websites/django_website_1/
sudo service apache2 restart
You should now be able to access your website via
www.myserver.com/django_website_1
If it is still now working check either the Apache error log for any errors
cat /var/log/apache2/error.log
or try to run your website locally and see what kind of errors you get:
/home/django_websites/django-virtualenv/bin/python /home/django_websites/django_website_1/manage.py runserver
If you want to add additional websites just add the folder containing the website files (and potentially a new virtualenv) to /home/django_websites/
and continue with 4., 5., and 6. When modifying the Apache config file, add the new text blog underneath the already existing one for the previous website. Do not mix/interleave these text blogs, but keep them separate in the config file, e.g.
Alias /media/ /home/django_websites/django_website_1/media/
Alias /django_website_1/static/ /home/django_websites/django_website_1/static/
<Directory /home/django_websites/django_website_1/static>
Require all granted
</Directory>
<Directory /home/django_websites/django_website_1/media>
Require all granted
</Directory>
<Directory /home/django_websites/django_website_1/core>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIDaemonProcess django_website_1 python-path=/home/django_websites/django_website_1:/home/django_websites/django-virtualenv/lib/python3.6/site-packages
WSGIProcessGroup django_website_1
WSGIScriptAlias /django_website_1 /home/django_websites/django_website_1/core/wsgi.py
###################
Alias /media/ /home/django_websites/django_website_2/media/
Alias /django_website_1/static/ /home/django_websites/django_website_2/static/
<Directory /home/django_websites/django_website_2/static>
Require all granted
</Directory>
<Directory /home/django_websites/django_website_2/media>
Require all granted
</Directory>
<Directory /home/django_websites/django_website_2/core>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIDaemonProcess django_website_2 python-path=/home/django_websites/django_website_2:/home/django_websites/django-virtualenv-2/lib/python3.6/site-packages
WSGIProcessGroup django_website_2
WSGIScriptAlias /django_website_2 /home/django_websites/django_website_2/core/wsgi.py
If you want to delete any of the website, just delete the folder (and accompanying virtualenvs) from the /home/django_websites/
folder and delete the corresponding text blog from the Apache config file.
The default Apache website, i.e. the website that is shows when you access
www.myserver.com
is located at
/var/www/html/index.html
You can update this file, e.g. by adding links to the various websites that you serve, in order to make it easier to access the different websites directly from the default webpage.
This concludes our guide to how to set up your system to serve multiple Django websites using different virtualenv with one Apache server. I hope this is useful for some of you. If you have any questions feel free to use the comments section below.
This guide was written since it took us a while to get this specific configuration running, i.e. serving multiple Django websites on a current Ubuntu OS while using different virtualenvs for the different websites. Many of the tutorials we found were for older versions (e.g. Ubuntu 14) and did not serve multiple Django websites with multiple virtualenvs using only one Apache server. ↩