How to Run Python and Ruby on Heroku with Multiple Buildpacks
One of my mentees here in codementor.io encountered a problem while running Python and Ruby together on Heroku. That session inspired me to write this tutorial—how to run more than one environment on Heroku.
PROBLEM
The project was working in Cloud9 without any problems. However, after deploying to Heroku, the log was displaying error messages about Python problems. Steve had written a scraper in Python, and it was not working. Steve, my mentee, had created an old test environment on Cloud9 for me to work on his problem.
SOLUTION
This solution uses this guideline from Heroku.
I solved the problem by using Heroku multi-buildpacks for Python and Ruby.
Heroku multi-buildpacks
Buildpacks are responsible for configuring your application. Heroku explains buildpacks in its documentation like this: "Buildpacks are composed of a set of scripts and depending on the programming language, the scripts will retrieve the dependencies, and output generated assets or compiled code, and more. This output is assembled into a slug by the slug compiler."
There are many Buildpacks (many can be seen here and here). There are distinct buildpacks for Ruby and Python.
So, If someone wants to deploy Ruby and Python together on Heroku, he needs to use “multiple buildpacks". So, Heroku would use different buildpacks to run your application. Each buildpack creates a distinct environment.
Running apps with Procfile
Steve’s application needs to run on Puma Server (the default web server of Ruby on Rails). So, I needed to create a Procfile
. Procfile runs commands automatically after the deployment. Procfiles are responsible for running shellcode on Heroku after deployment. For example, you can start web servers and long-running tasks. See more from here.
So, I created a Procfile file like this on his project's root:
# Procfile
web: bundle exec rails server -p $PORT
worker: python scraper.py
In multiple buildpacks, the order of the buildpacks is critical.
I needed to add Ruby buildpack as the last one because Steve would be running a Puma web server for Ruby. If I had not done it like that, Steve would not be able to run his Ruby server on Heroku. So, I added Python buildpack first and then added Ruby buildpack later. Together, they become "the mighty multiple buildpack," ready to use on Heroku.
# command line commands
heroku buildpacks:clear # remove all of the buildpacks first
heroku buildpacks:add heroku/python # add the python buildpack first
heroku buildpacks:add heroku/ruby # add the ruby buildpack second
heroku buildpacks # to see what I have done
# === golden-ratio-1618 Buildpack
# 1. heroku/python
# 2. heroku/ruby
Internals of a Buildpack
To know more about the internals of a Buildpack, you can review the Github source of a Buildpack, for example, Ruby. bin/detect
file is essential. You can see the source code of this file to see what is needed for Heroku to deploy your app. For example, for Ruby, you need to create a Gemfile
in the root directory of your app, or it will fail.
Required packages were missing
However, after adding all of the buildpacks, the project was not functioning on Heroku, yet. That was because of Python requirements.txt
file. The file is responsible for adding the required Python packages to your app to be installed on Heroku automatically after each deployment (like Gemfile
in Ruby). Every time you deploy, Heroku launches a new instance and initializes it by running those files. They are the required files for the environments, like Python and Ruby.
I also needed to verify the behavior in the local, so I ran pip install -r requirements.txt
as a command to install the required packages to the local.
Moreover, I tested the setup with: heroku local
command. See more about it here.
I put the required packages by looking to Python scraper’s header code. One of them was lxml
. I found its latest version number by looking at its github page (to the tags).
So, we created a requirements.txt file like this:
# requirements.txt file
lxml==3.6.4
So, after delivering the training session, Steve responded like this:
Conclusion
With this article, my aim was that trying to show how I handled a mentee's request and let you know about how to run multiple-buildpacks on Heroku. Of course, there are many details that I skipped over. Maybe, those can be the next article's topics. Similarly, you can also read more tutorials about Heroku, like: how to parse dashboard Heroku or how to get push notifications when your Heroku app is deployed. See you next time.
Thank you for the post, looks like a great resource that I’d like to use for my app. Could you provide any information or hint what to look for when figuring out how those two languages should communicate with each other? If there were separate apps I’d use API calls but I’m not sure what is the best solution when they’re technically the same app. Any advice will be greatly appreciated!
Hi Joanna. Good question! You can make them talk through a database, with a messaging server like rabbitmq. That totally depends on your application. There are many ways to make them talk, I gave you only two, the most simple examples.
Hello, your tutorial is very clear but it does’t word for me :(
I’ve a rails app and a python script with a requirement.txt. My problem is to know where I have to put the python script. Do I need to put it in the rails lib and the requirement.txt in the rails app root ? because I do that and it does’t work.
So where did you put your python script and requirement.txt to work on heroku whith the rails app ?
thanks to you :)
Matt
Is there any error you are getting? I had the same scenario which I needed to run python script within ruby on rails and it worked for me.
It’s
requirements.txt
notrequirement.txt
. Better to put it into the root folder of your project. You can put python scripts into any directory that you like, it does not matter.You may look at this repo from Heroku: https://github.com/heroku/python-getting-started
The tutorial for it is here as well: https://devcenter.heroku.com/articles/getting-started-with-python#introduction