Skip to content

Installing a Python web application in an Incus container

Note: Incus is a community-fork of LXD

Step 1: minimal viable

  1. Enter the container with sudo /etc/cncz/bin/incusexec x

  2. Install the Apache web server: sudo apt-get install apache2, and activate the option to forward incoming requests: sudo a2enmod proxy_http . Installing Apache should make Apache's default page visible at x.cls.ru.nl (or in some cases x.rich.ru.nl).

  3. Install Git and Python pip sudo apt-get install git python3-pip:

  4. Install virtualenv and create an environment in /var/www/: pip3 install virtualenv; virtualenv -p python3 /var/www/env

  5. In /var/www/ , git clone your code into a repo folder, and get it running. You most likely want to install all dependencies with pip install -r requirements.txt. Before installing any dependencies, make sure you are inside your environment (which you can enter with source /var/www/env/bin/activate)

  6. Run your application inside a GNU screen (if you don't know how to use GNU screen, this it the time to learn :) ). In the example below we will be assume you are using the Django default port 8000, but this can of course be your favorite integer.

  7. Tell Apache to forward all incoming requests to you application process, by adding this line to the config file /etc/apache2/sites-available/000-default.conf (inside the VirtualHost):

    ProxyPass / http://localhost:8000/
    

  8. Restart Apache: /etc/init.d/apache2 restart

Step 2: stable with uwsgi

  1. Create a writable folder and a logs folder within it, and make sure they are writable by the ubuntu user:
    mkdir /var/www/writable
    mkdir /var/www/writable/logs
    chown -R ubuntu /var/www/writable/
    chgrp -R ubuntu /var/www/writable/
    
  2. Install uwgsi: pip3 install uwsgi

  3. Activate the option to forward incoming requests to uwsgi: sudo a2enmod proxy_uwsgi

  4. Tell Apache to forward all incoming requests to you application process, by adding this line to the config file /etc/apache2/sites-available/000-default.conf (replaces the previous proxy pass line from step 1):

    ProxyPass / uwsgi://localhost:8000/
    

  5. Restart Apache: /etc/init.d/apache2 restart

  6. Create a UWSGI config file named x.ini with content similar to this, where x is the name of your project:

    [uwsgi]
    socket = 127.0.0.1:8000
    chmod-socket = 775
    chdir = /var/www/repo/x
    master = true
    binary-path = /usr/bin/uwsgi
    virtualenv = /var/www/env
    module = x.wsgi:application
    uid = ubuntu
    gid = www-data
    processes = 1
    threads = 1
    plugins-dir = /usr/lib/uwsgi/plugins
    logger = file:/var/www/writable/logs/x.uwsgi.log
    wsgi-file = /var/www/repo/x/wsgi.py
    env = DJANGO_SETTINGS_MODULE=x.settings
    static-map = /static=/var/www/repo/x/static
    
    You can then run the web application with uwsgi x.ini.

It's normal that it takes some time to get all the path set up correctly. The first step is to get the logging working, so uwsgi can tell you what is the next problem it encountered. As you can see in the config file, logs are written to the writable folder.

Step 3: reliable with rc.local

Now we want the uwsgi process to run automatically. There are two ways to do this:

Option 1: Start a screen session in a cronjob

  1. Install GNU screen with apt-get install screen

  2. Do crontab -e and add this to your cron file

    @reboot     screen -dmS my-screen-name bash -c "cd /var/www/x/repo && uwsgi my-webapp.ini"
    

Option 2: Start an independent process with rc.local

  1. Create a /etc/rc.local file:
    #!/bin/bash
    
    source /var/www/env/bin/activate
    uwsgi /var/www/x.ini &
    exit 0;
    
  2. Make the file executable: chmod a+x /etc/rc.local

  3. Test it by simpling running sudo /etc/rc.local in your terminal.

  4. Now test it by restarting your container. To achieve this, leave the container, so you are in Lightning again, and go sudo /etc/bin/cncnz/bin/incusstop x; sudo /etc/bin/cncnz/bin/incusstart x;

Although rc.local is run as root, not the same environment variables are set. Therefore, you have to set them in rc.local explicitly, e.g.

JAVA_HOME=/root/.sdkman/candidates/java/current
export JAVA_HOME
Next time the container reboots, the application should be available automatically.