This post is about building a Debian package to deploy a Django application on a Debian server.

The package will take care of the following:

  • Install application
  • Web server configuration (in our case: Lighttpd with fastcgi)
  • Init.d script to start/stop Django server

We will also use a debconf template to ask 1 question to the user to make our package more dynamic.

Let’s assume you have a Django application located in /usr/share/djangoapp . This is where you have settings.py, manage.py, views.py etc…

First thing, we need to create a folder debian and copy the Django app folder into it:

mkdir ./debian
mkdir -p ./debian/usr/share/
cp -r /usr/share/djangoapp ./debian/usr/share/

We also need to create a folder names DEBIAN in debian/

mkdir ./debian/DEBIAN

DEBIAN files

This folder needs to contain the following files:

  • control
  • config
  • conffiles
  • templates
  • postinst
  • postrm
  • preinst
  • prerm

control

This is the most important file in a Debian package. It gives information on the package itself along with some dependencies. In our case, it looks like this:

Package: djangoapp
Version: 0.1-1
Section: devel
Priority: optional
Architecture: all
Depends: python, python-django, python-flup, debconf, lighttpd
Maintainer: Laurent Luce <laurent@tomnica.com>
Description: short description
 long description

Depends consists of a list of packages required for our package to run properly.

config

config is a script responsible for asking questions to the user before the package gets installed. In our case, we will ask one simple question: what is your full name?

#!/bin/sh -e

# Source debconf library.
. /usr/share/debconf/confmodule

# server type?
db_input critical djangoapp/username || true
db_go

As you can see, you need to include debconf confmodule to add configuration support. Our question is critical.

Wait a minute, where is the question definition? It is defined in the templates file

templates

This is where we define our question: string type.

Template: djangoapp/username
Type: string
Description: Username:
This package requires your full name.

conffiles
This file contains the configuration files generally located under /etc . We add our Django start script and the lighttpd configuration file.

/etc/init.d/django
/etc/lighttpd/lighttpd.conf

preinst
This script is run before the package gets installed.

#!/bin/bash
set -e

# stop django server
if [ -f /etc/init.d/django ]
then
  invoke-rc.d django stop
fi

postinst
This script is run after the package is unpacked.

#!/bin/sh
set -e

# Source debconf library.
. /usr/share/debconf/confmodule

db_get djangoapp/username
username="$RET"
# do what you want with this username

# register django
update-rc.d django defaults 90 >/dev/null
# start django
invoke-rc.d django start

db_stop

postrm
This script is run after the package is removed.

#!/bin/bash
set -e

if [ "$1" = "purge" -a -e /usr/share/debconf/confmodule ]; then
    # Source debconf library.
    . /usr/share/debconf/confmodule
    # Remove my changes to the db.
    db_purge
fi

if [ "$1" = "remove" ]; then

# Source debconf library.
. /usr/share/debconf/confmodule

# remove Django start script
update-rc.d -f django remove

# Remove my changes to the db.
db_purge
fi

prerm
This script is run before the package is removed.

#!/bin/bash
set -e

# stop django server
invoke-rc.d django stop

Web server configuration

Here is what needs to be modified in the web server configuration (in our case, lighttpd.conf):

mod_fastcgi needs to be added to the server modules.

The Django server will be running on port 3033 so we need to specify the connection here:

fastcgi.server = (
  "/djangoapp.fcgi" => (
    "main" => (
      # Use host / port instead of socket for TCP fastcgi
      "host" => "127.0.0.1",
      "port" => 3033,
      "check-local" => "disable",
  ))
)

Django init.d script

Now is time to add our Django start/stop script:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          FastCGI servers for Django
# Required-Start:    networking
# Required-Stop:     networking
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Django FastCGI
#

DJANGO_SITE="djangoapp"
SITE_PATH=/var/www
RUNFILES_PATH=$SITES_PATH/tmp
HOST=127.0.0.1
PORT_START=3033
RUN_AS=www-data

set -e

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="Django FastCGI"
NAME=$0
SCRIPTNAME=/etc/init.d/$NAME

#
#       Function that starts the daemon/service.
#
d_start()
{
    # Starting all Django FastCGI processes
    echo -n ", $DJANGO_SITE"
    if [ -f $RUNFILES_PATH/$DJANGO_SITE.pid ]; then
        echo -n " already running"
    else
       start-stop-daemon --start --quiet \
                       --pidfile $RUNFILES_PATH/$DJANGO_SITE.pid \
                       --chuid $RUN_AS --exec /usr/bin/env -- python \
                       $SITE_PATH/$DJANGO_SITE/manage.py runfcgi \
                       host=$HOST port=$PORT \
                       pidfile=$RUNFILES_PATH/$DJANGO_SITE.pid
    fi
}

#
#       Function that stops the daemon/service.
#
d_stop() {
    # Killing all Django FastCGI processes running
    echo -n ", $DJANGO_SITE"
    start-stop-daemon --stop --quiet --pidfile $RUNFILES_PATH/$SITE.pid \
                          || echo -n " not running"
    if [ -f $RUNFILES_PATH/$DJANGO_SITE.pid ]; then
        rm $RUNFILES_PATH/$DJANGO_SITE.pid
    fi
}

ACTION="$1"
case "$ACTION" in
    start)
        echo -n "Starting $DESC: $NAME"
        d_start
        echo "."
        ;;

    stop)
        echo -n "Stopping $DESC: $NAME"
        d_stop
        echo "."
        ;;

    restart|force-reload)
        echo -n "Restarting $DESC: $NAME"
        d_stop
        sleep 1
        d_start
        echo "."
        ;;

    *)
        echo "Usage: $NAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac

exit 0

This script needs to be placed in ./debian/etc/init.d/

You will also need to create a link from /var/www/djangoapp to /usr/share/djangoapp

changelog, changelog.Debian and copyright

Those are required for Debian packages:

changelog

djangoapp (0.1-1) unstable; urgency=low

  * Initial release.
    + Initial package

 -- Laurent Luce <laurent@tomnica.com>  Fri, 01 Jan 2010 00:00:00 +0000

In our case, changelog.Debian is identical to changelog.

copyright

DjangoApp

Copyright: bla bla

2010-01-01

The home page of xxx is at: 
http://www.xxx.com

Copyright xxx 2010

We need to place those files properly:

mkdir -p ./debian/usr/share/doc/djangoapp
cp changelog changelog.Debian copyright ./debian/usr/share/doc/djangoapp/
gzip --best ./debian/usr/share/doc/djangoapp/changelog
gzip --best ./debian/usr/share/doc/djangoapp/changelog.Debian

Build package

We need to fix the permission of our package files:

find ./debian -type d | xargs chmod 755

We build the package and verify it:

fakeroot dpkg-deb --build debian
mv debian.deb DjangoApp_0.1-1_all.deb
lintian DjangoApp_0.1-1_all.deb

Our package is now ready to be deployed.

If you enjoyed this article, check out my web app Gourmious and discover and share your favorite restaurant dishes.

Last modified: May 30, 2020

Author

Comments

Thank you very much for this tutorial!

oh my “bip” this tut article is ‘bip’ing awesome! i creating a deb package, and that’s ‘bip’ing easy! tnx 😡

Hello, thanks for the explaination, but can you please add more details how to do the webserver configuration? I think there is missing one step, cant figure out where put make the lighttpd.conf modifications. Greetings Willi

    Author

    You need to do the same as for /etc/init.d/django. e.g /etc/lighttpd/lighttpd.conf. I updated the tutorial to reflect that.

Thanks a lot for such detailed instruction!

hello, and thanks for your tutorial. I try to build a Debian package with simple files : – a JAVA app (.jar) – launchers in /usr/bin/myapp (just a simple Bash script) – autostart for X sessions : /etc/xdg/autostart/myapp.desktop – some files like icons that go to /usr/share… – modifying Gnome and KDE menus to insert myapp So i don’t have sources to compile. The weird thing is that on a parc with near 200 PC, my package installed successfully on all. But some PC don’t have anything except the directory /opt/myapp where the JAVA files are in. I don’t understand how to force it to copy files to /etc/… and /usr… Do you have some ideas about that please ? I’m running Debian testing (Squeeze).

    Author

    @survietamine I am a bit confused, do you mean those machines don’t have the folders /etc, /usr etc… or do you mean your app files are not copied in those folders? The Debian file conffiles allows you to specify which files are configuration files and any files under /etc/ should be added to that file. For example, this is where I specified /etc/init.d/django which the script to start/stop the Django server instances.

Comments are closed.