Last update on .

I finished my graduate degree last fall, and I knew it was time to upgrade my server. I haven't really touched it since October 2012, and I have grown increasingly paranoid that my server will get hacked due to the out-of-date web applications. So I finally carved out a weekend and got to work. Most of my main site is running Django Blog Zinnia.

I upgraded:

  • from Debian 7 (Wheezy) to Debian 8 (Jessie)
  • from Apache 2.2 to Apache 2.4
  • from Django 1.4 to 1.10
  • from Python 2.7 to Python 3.4
  • from Zinnia 0.11 to 0.18.1

As is common in technology, it's a shame to upgrade now because Debian 8 Stretch is getting close, and Django 1.11 is nearly ready and will be an LTS. I'll need to do another round of upgrades in the summer.

These are the main complications I encountered while upgrading:

  • Debian now adds a ".conf" to the end of Apache configuration files. If you forget, the file is silently ignored.
  • You need to be careful to match the right version of Zinnia Blog to Django. See
  • The pip included with Python 3.4 is broken, so:
    python3 -m venv --without-pip site_venv
    curl | ./site_venv/bin/python

(this is fixed on Python 3.5)

  • The Apache upgrade required migrating the access control syntax.
  • It took me some fiddling to figure out the right way to specify WSGI as a Daemon with proper isolation between my various web applications where the environment for plaintext and SSL are shared:

    ## Under VirtualHost *:80 ##
    # Setup Django via WSGI in the same process as for non-SSL
    <Directory "/srv/www/">
        Require all granted
    WSGIDaemonProcess python-path=/srv/www/
    WSGIScriptAlias / /srv/www/
    ## <snip> then under VirtualHost *:443 ##
    # Setup Django via WSGI
    <Directory "/srv/www/">
        Require all granted
    WSGIScriptAlias / /srv/www/
  • I had some really strange errors that resulted from not upgrading from libapache2-mod-wsgi tolibapache2-mod-wsgi-py3. I finally diagnosed this by looking at the Python libraries path and realizing the Python 2 libraries were being pulled in and mixed with my Python 3 libraries.

  • Going from South to Django Migrate: The instructions state that you must run South to upgrade the database to Django 1.6, then upgrade Django to 1.7, and upgrade the rest of the way using Django Migrate. However, due to the challenge of getting the right version of Zinnia to match the version of Django, I couldn't get the migration to work. Instead, ended up writing an Extract-Transform-Load (ETL) migration script which I published.

    • I got a lot of strange thread timeouts during import. It took me a while to figure out they were from failed Pingbacks. I needed to disable Zinnia's Pingback service in
    • The hardest part of the ETL script was that saving a Django model class does not increment the PostgreSQL pk sequence number. Everything looks successful until you try to add a new item to the live site and you get errors about duplicate key value violates unique constraint. There are a number of ways to deal with this problem, but the one I settled on was to complete the import and then manually set the sequence number with a call like

      # Fix postgresql sequence id's
      max_id = Entry.objects.all().aggregate(max=Max('id'))['max']
      print("Setting max Entry id to: %s" %(max_id+1))
      django_cursor = django_db.cursor()
      result = django_cursor.execute('ALTER SEQUENCE zinnia_entry_id_seq '
                                     'RESTART %s', [max_id+1])
    • I then had to update the templates and CSS to match the new Zinnia themes. One note is that the Zinnia breadcrumbs are required in the template or paging will fail. As you page through the list of blog posts, the page number is appended to the breadcrumb.

    • I also noticed that the Zinnia documentation on SiteMaps is out-of-date, so I reported a documentation fix.

On the whole, I think things went pretty smooth. While I was at it:

  • I setup an Apache redirect for maintenance

    #  # Enable maintenance redirect
    #  <Directory "/srv/www/">
    #    <Files maintenance.html>
    #      Require all granted
    #    </Files>
    #  </Directory>
    #  RedirectMatch ^(?!.*maintenance.html).*$
  • I modernized the environment to use a Python virtual environment,

  • The environment pulls configuration from the environment to know if it should be in DEBUG mode (see rule 3 of a 12 Factor App)
  • I learned how to use the Python mock email server to trap Django emails when I'm in development: python -m smtpd -n -c DebuggingServer localhost:1025
  • And now I have a completely independent development environment on my laptop (Fedora 24 running Python 3.5).

No more working on the production machine for my little blog!

Zinnia has come a long way. My favorite new feature is being able to edit with MarkDown. It appears to use the modified MarkDown with the StackOverflow and GitHub goodness like bare URLs. In addition, the base theme is cleaner and there is generally more polish. I keep finding new capabilities and I haven't had time to play with them all yet.

I am thrilled that Django Blog Zinnia is still maintained after four years. I hope that the project continues to grow and I look forward to contributing back more than I have in the past.


Pingbacks are closed.


Comments are closed.