Install a web server for Python Applications

#Python#Nginx#Django#Ubuntu#Debian#Supervisor#Gunicorn

by Maxime Decooman

Note: This setup is done on a brand new server instantiated at Digitalocean(DO). Once you have added your ssh key in the DO Droplet panel at creation, you will login as root. If you are not root, you need to be in the sudoer group and use the sudo command. I have written it for convenience.

Install Python 3.13 on Ubuntu 24.04

Update first your package manager and packages lists:

sudo apt update && sudo apt upgrade
  • Reboot
  • Make sure you have all these libraries to have a successful build (curl, xz-utils and wget are usually part of the distribution already):
sudo apt install -y make build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev llvm libncurses5-dev \
libncursesw5-dev tk-dev curl xz-utils wget

Donwload the latest python (3.13.1 at the time of this update):

cd /usr/src
sudo wget https://www.python.org/ftp/python/3.13.1/Python-3.13.1.tgz

Extract archive:Once the download is finished, extract the archive file content.

sudo tar -xzf Python-3.13.1.tgz

Prepare the source code and use the '--enable-optimizations' option witch enable additional supports like SSL and bz2.

cd Python-3.13.1
sudo ./configure --enable-optimizations

Compile and install. Use 'altinstall', to install it as a separate Python avoiding overwriting the default Python installation.

sudo make -j4 && sudo make altinstall

or

sudo make && sudo make altinstall

Note: make -j4, the -j option followed by a number (in this case, 4) tells make to execute multiple jobs concurrently. This means make will run up to four jobs in parallel. If you have a multi-core processor, using -j with a number equal to or slightly more than the number of CPU cores can significantly speed up the compilation process.

Have a cup of coffee, or two...

Test all is fine

python3.13 --version
# > python3.13.1

the current version of python is

python --version
# > ptyhon 3.13.1

update-alternatives --install (optional)

You can manage which version of Python is invoked when you use the python3 command. Personally I do not modify the python3 command on production servers and leave it to the default system version. However on other systems I may use it temporarilly for different purposes:

Add the symlinks

# update-alternatives --install <link> <name> <path> <priority>
# Add the symlinks
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 110
sudo update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.13 100

Check all went smoothly

sudo update-alternatives --display python3
# > python3 - auto mode
# >  link best version is /usr/bin/python3.10
# >  link currently points to /usr/bin/python3.10
# >  link python3 is /usr/bin/python3
# >/usr/bin/python3.10 - priority 110
# >/usr/local/bin/python3.13 - priority 100

Change which symlink will answer to python3 command:

sudo update-alternatives --config python3

Note: to undo

sudo update-alternatives --remove python /usr/local/bin/python3.13

Have a walk...

Create a django project

Add a user

Our user for the sake of this article is called Julius.

adduser julius
# You'll be prompted to enter and confirm a password for the new user, as well as to fill out some additional optional information.
  • Grant sudo privileges to allow the new user to perform administrative tasks, add them to the sudo group.
usermod -aG sudo julius

Switch to the New Account

Switch to the newly created user account with the following command:

su - julius
# check you are in the correct directory that should be /home/julius
pwd

Install a Python virtual environment and create a new Django project

# create a project directory
mkdir mysite

# Go into the directory
cd mysite

# install the virtual environment in a hidden folder .venv
python3.12 -m venv .venv

Activate the virtual environment:

source .venv/bin/activate

Install Django

With the virtual environment activated, you can now install Django.

pip install django

Start a new Django project by running:

# Replace mysite with your project's name.
django-admin startproject mysite .

# The period at the end is to create the project in the same directory without creating a new one with the project name.

Edit the settings (i use vi or vim, use nano orthe editor you like):

vi mysite/settings.py

Change the line ALLOWED_HOSTS = [] with:

# ALLOWED_HOSTS = []
ALLOWED_HOSTS = [localhost, server_ip_address, mysite.com]

# server_ip_address: the IP address of your server (not advised in production).
# mysite.com: the name of your server url in your domain DNS server

Or, if you are lazy, you can also set * but don't do that in production:

ALLOWED_HOSTS = ['*']

Run the Development Server

At this point, you should have a (.venv) as a prefix of your terminal prompt now: Image

  • Change into your project directory and run Django's development server.
cd mysite
python manage.py runserver 0.0.0.0:8000

Your Django project should now be accessible via http://your_server_ip:8000 in a web browser, If you cannot access, verify you do not have a firewall installed and open the TCP port 8000 (for the time of the test)

Image

When you are finished exploring, hit CTRL-C in the terminal window to shut down the development server.

Install and test gunicorn

Install gunicorn

pip install gunicorn
# in /home/julius/mysite
cd ~/mysite  # if you went away
gunicorn --bind 0.0.0.0:8000 mysite.wsgi

Image

Like you did previously, you should be able to access your site at http://your_server_ip:8000 but running under gunicorn this time. Image

Note: Deactivate your Python virtual environment when you're done by executing:

deactivate

The next steps will cover how to configure a web server (Nginx) that will serve as a proxy to the WSGI application server (Gunicorn).

Install and configure Nginx

Install Nginx

sudo apt install nginx

make nginx to start automatically

sudo systemctl enable nginx

Create the config file for your site

sudo vi /etc/nginx/sites-available/mysite

Here is a minimalistic config file that should suffice for this tutorial:

#  /etc/nginx/sites-available/mysite
server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/julius/mysite;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn-mysite.sock;
    }
}

Save and close the file once done. You need to enable the site so nginx will start listening for requests. You enable the file by linking it to the sites-enabled directory:

sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled

Test your configuration for syntax errors:

sudo nginx -t

If no errors you can restart Nginx:

sudo systemctl restart nginx

Setting up supervisor

Install supervisor

sudo apt install supervisor

Make supervisor to start automatically with the system

sudo systemctl enable supervisor

Create a supervisor configuration file using vi editor (or the one you prefer).

sudo vi /etc/supervisor/conf.d/mysite.conf

Add the following configuration to the file. The details are matching the example used for mysite path of configuration and the user. Using this configuration, you will be running your Python project with Gunicorn, and instead of using the IP address for your application, you will be running the project like a sock file.

[program:mysite]
command=/home/julius/mysite/.venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn-mysite.sock mysite.wsgi:application
directory=/home/julius/mysite
user=root
group=www-data
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/mysite.log
stderr_logfile=/var/log/supervisor/mysite-error.log

Save and close the file when you are done.

Now restart the supervisor and nginx services using the below commands to apply new changes.

sudo systemctl restart supervisor
sudo systemctl restart nginx

# check supervisor is running correctly
systemctl status supervisor

Image Another useful command to check and verify the list of processes under the supervisor. You should see the “mysite” process running.

sudo supervisorctl status
#> mysite                           RUNNING   pid 79290, uptime 0:01:09

Finally, you can now visit your site at http://your_server_ip_or_domain_name, which is currently being served by Gunicorn and proxied by Nginx.

Have a big treat, you did it so far 🚀...

There are still a "few" things to do, especially to secure the setup and enable https but that is enough to get you started.