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: email@example.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/ | firstname.lastname@example.org: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 email@example.com:tranquil-tundra-8486.git
Now we can try our push again:
randomdrake:~/myapp$ git push heroku master The authenticity of host 'heroku.com (184.108.40.206)' 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,220.127.116.11' (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.