Programming

The documentation from Heroku on how to start and deploy a Django application is quite excellent. Unfortunately, there’s not a lot of information out there on how to deploy something you’ve already written. In addition, the guide assumes you already have a decent amount of knowledge surrounding things like virtualenv. Because of this, I thought I’d go ahead and document the process this time instead of just angrily Tweeting about the problems and misunderstandings. Please feel free to comment any additional problems, or nuances you run into regarding doing this on Ubuntu.

First thing’s first, installing the Heroku toolbelt. While the simple command they give you seems pretty straightforward, there’s a small problem. They mention the following:

“The heroku command line client will be installed into /usr/local/heroku and /usr/local/heroku/bin will be added to your PATH.”

That’s all good except they don’t actually add it your $PATH for you. They add it to your ~/.bashrc file:

### Added by the Heroku Toolbelt
export PATH="/usr/local/heroku/bin:$PATH"

Because of this, you’ll need to restart your terminal or add it manually before you can easily complete the Heroku login instructions.

Alright, let’s go ahead and get logged in:

randomdrake:~$ heroku login
Enter your Heroku credentials.
Email: david@randomdrake.com
Password (typing will be hidden): 
Authentication successful.

Excellent, we’re logged in. Now it’s time to let Heroku know what we need installed to run our app. The first thing that the Heroku guide instructs you through is creating your necessary requirements.txt file. One can do this by running the following command:

randomdrake:~$ pip freeze > requirements.txt

Unfortunately, just performing this outright in your Ubuntu environment produces an unnecessarily large requirements.txt

randomdrake:~$ cat requirements.txt
Brlapi==0.5.6
Django==1.4.1
GnuPGInterface==0.3.2
Mako==0.5.0
MarkupSafe==0.15
PAM==0.4.2
PIL==1.1.7
Twisted-Core==11.1.0
Twisted-Names==11.1.0
Twisted-Web==11.1.0
UniConvertor==1.1.4
adium-theme-ubuntu==0.3.2
apt-xapian-index==0.44
apturl==0.5.1ubuntu3
argparse==1.2.1
boto==2.6.0
...
...

Because of this, we need to create a virtualenv to nail down our requirements. If you don’t have virtualenv, yet. You can get it get it through either of the following commands:

randomdrake:~$ sudo apt-get install python-virtualenv 

or

randomdrake:~$ sudo pip install virtualenv

Now that we have virutalenv installed, we can go about starting it up, installing our requirements, and getting an accurate requirements.txt. When I’m doing this step, I like to go to my settings.py file in my app to look and see what’s installed under INSTALLED_APPS. It will probably look something like this:

INSTALLED_APPS = ( 
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'website',
    'reviews',
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'storages',
    'registration',
    'taggit',
)

This gives us an idea of what we’re going to need to install in our virtual environment. Let’s get started creating our virutal environment and installing those requirements. Go ahead and cd into the root of the directory where your app lives. From there, we want to follow the Heroku documentation:

randomdrake:~/myapp$ virtualenv venv --distribute
New python executable in venv/bin/python
Installing distribute.............................................................................................................................................................................................done.
Installing pip...............done.
randomdrake:~/myapp$ source venv/bin/activate
(venv)randomdrake:~/myapp$ pip install Django psycopg2 dj-database-url
Downloading/unpacking Django
  Downloading Django-1.4.1.tar.gz (7.7Mb): 7.7Mb downloaded
  Running setup.py egg_info for package Django
    
Downloading/unpacking psycopg2
  Downloading psycopg2-2.4.5.tar.gz (719Kb): 719Kb downloaded
  Running setup.py egg_info for package psycopg2
    
    no previously-included directories found matching 'doc/src/_build'
Downloading/unpacking dj-database-url
  Downloading dj-database-url-0.2.1.tar.gz
  Running setup.py egg_info for package dj-database-url
    
Installing collected packages: Django, psycopg2, dj-database-url
  Running setup.py install for Django
    changing mode of build/scripts-2.7/django-admin.py from 664 to 775
    
    changing mode of /home/randomdrake/myapp/venv/bin/django-admin.py to 775
  Running setup.py install for psycopg2
    building 'psycopg2._psycopg' extension
    gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -DPSYCOPG_DEFAULT_PYDATETIME=1 -DPSYCOPG_VERSION="2.4.5 (dt dec pq3 ext)" -DPG_VERSION_HEX=0x090106 -DPSYCOPG_EXTENSIONS=1 -DPSYCOPG_NEW_BOOLEAN=1 -DHAVE_PQFREEMEM=1 -I/usr/include/python2.7 -I. -I/usr/include/postgresql -I/usr/include/postgresql/9.1/server -c psycopg/psycopgmodule.c -o build/temp.linux-x86_64-2.7/psycopg/psycopgmodule.o -Wdeclaration-after-statement
    
....
    
    no previously-included directories found matching 'doc/src/_build'
  Running setup.py install for dj-database-url
    
Successfully installed Django psycopg2 dj-database-url
Cleaning up...
(venv)randomdrake:~/myapp$

If you are familiar with the additional packages you need to install from your INSTALLED_APPS listing, go ahead and install them now. To see if you have successfully installed everything, try to run your server:

(venv)randomdrake:~/myapp$ python manage.py runserver
Error: No module named taggit

We can see that we are missing the taggit module. So we’ll go ahead and install that too.

(venv)randomdrake:~/myapp$ pip install django-taggit
Downloading/unpacking django-taggit
  Downloading django-taggit-0.9.3.tar.gz
  Running setup.py egg_info for package django-taggit
    
Installing collected packages: django-taggit
  Running setup.py install for django-taggit
    
Successfully installed django-taggit
Cleaning up...
(venv)randomdrake:~/myapp$

Keep on doing this until you can successfully run your server:

(venv)randomdrake:~/myapp$ python manage.py runserver
Validating models...

0 errors found
Django version 1.4.1, using settings 'myapp.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Now that we know we can run our server, we can freeze our requirements:

(venv)randomdrake:~/myapp$ pip freeze > requirements.txt

Take a look and see that we now have a nice, succinct list of requirements that we need for our particular app:

(venv)randomdrake:~/myapp$ cat requirements.txt 
Django==1.4.1
argparse==1.2.1
distribute==0.6.24
dj-database-url==0.2.1
django-registration==0.8
django-storages==1.1.5
django-taggit==0.9.3
psycopg2==2.4.5
wsgiref==0.1.2

You can feel free to leave your virtual environment now by issuing the deactivate command. You can also go ahead and remove the ./venv directory that was created with a little rm -rf venv action.

Before we go ahead and create our Heroku app, let’s let it know we want to use a different WSGI than the default. For some reason, they don’t document the process until after you’ve gotten your app up and running which, is a bit odd, considering it’s widely suggested to use an alternate. Follow the directions in the guide regarding “Using a Different WSGI Server” to setup gunicorn.

I personally like to setup a few gunicorn workers. So, I add a Procfile in the application root with the following:

web: gunicorn exampleapp.wsgi -b 0.0.0.0:$PORT -w 10

This will ensure your Heroku installation has a few workers available to accept requests. Now we can go ahead and create our Heroku app! Hopefully you’ve already got your source code in a github repo. If not, get your own repo setup with your code first as you won’t be able to access your code as it’s committed to the Heroku repo. Now let’s follow the directions from the Heroku guide:

randomdrake:~/myapp$ heroku create
Creating tranquil-tundra-8486... done, stack is cedar
http://tranquil-tundra-8486.herokuapp.com/ | git@heroku.com:tranquil-tundra-8486.git
randomdrake:~/myapp$ git push heroku master
fatal: 'heroku' does not appear to be a git repository
fatal: The remote end hung up unexpectedly
randomdrake:~/myapp$

Well crap, that didn’t work quite as smoothly as expected. This is because Heroku isn’t recognized as a remote repository. You can see the remote repositories you have setup already:

randomdrake:~/myapp$ git remote -v
origin	https://github.com/randomdrake/myapp.git (fetch)
origin	https://github.com/randomdrake/myapp.git (push)

As one can plainly see, Heroku is not one of the ones listed there. No worries, we can add it as a remote option. When we did the heroku create command we were assigned a random app name. In our example: neat-narfy-4242. We can use this to add our correct remote heroku host:

randomdrake:~/myapp$ git remote add heroku git@heroku.com:tranquil-tundra-8486.git

Now we can try our push again:

randomdrake:~/myapp$ git push heroku master
The authenticity of host 'heroku.com (50.19.85.132)' can't be established.
RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'heroku.com,50.19.85.132' (RSA) to the list of known hosts.

At this point, you may get an error like:

Permission denied (publickey).
fatal: The remote end hung up unexpectedly

If this is the case, it means you haven’t added your public key to Heroku, yet. Assuming you have already generated your keys for your system in your home directory, you can add your key with a simple command:

randomdrake:~/myapp$ heroku keys:add ~/.ssh/id_rsa.pub

Now you should be able to succcessfully deploy your app following the remaining instructions in the guide.

I hope that helped out. If you have additional issues doing this in Ubuntu 12.04, or just want to leave a message, feel free to comment below.

If you liked this post, consider subscribing to my feed or following me on Twitter at @randomdrake.

  • Déborah Mesquita

    Thank you SO MUCH!

  • So this post is 4 years old, but it saved me so much time. Thanks!