In several different articles now, we’ve explored how to build web apps using Haskell. See for instance, our Haskell Web Series and our API integrations series. But all this is meaningless in the end if we don’t have a way to deploy our code so that other people on the internet can find it! In this next series, we’ll explore how we can use common services to deploy Haskell code. It’ll involve a few more steps than code in more well-supported languages!
If you’ve never programmed in Haskell at all, you’ve got a few things to learn before you start deploying code! Download our Beginners Checklist for tips on how to start learning! But maybe you’ve done some Haskell already, and need some more ideas for libraries to use. In that case, take a look at our Production Checklist for guidance!
Heroku uses the concept of a “buildpack” to determine how to turn your project into runnable code. You’ll deploy your app by pushing your code to a remote repository. Then the buildpack will tell Heroku how to construct the executables you need. If you specify a Node.js project, Heroku will find your
package.json file and download everything from NPM. If it’s Python, Heroku will install
pip and do the same thing.
Heroku does not have any default buildpacks for Haskell projects. However, there is a buildpack on Github we can use (star this repository!). It will tell our Heroku container to download Stack, and then use Stack to build all our executables. So let’s see how we can build a rudimentary Haskell project using this process.
We’ll need to start by making a free account on Heroku. Then we’ll download the Heroku CLI so we can connect from the terminal. Use the
heroku login command and enter your credentials.
Now we want to create our application. In your terminal,
cd into the directory that has your Haskell Stack project. Make sure it’s also a Github repository already. It’s fine if the repository is only local for now. Run this command to create your application (replace
haskell-test-app with your desired app name):
heroku create haskell-test-app \
-b argument specifies our buildpack. We'll pull it from the specified Github repository. If this works, you should be able to go to your Heroku dashboard and see an entry for your new application. You’ll have a Heroku domain for your project that you can see on project settings.
Now we need to make a Procfile. This tells Heroku the specific binary we need to run to start our web server. Make sure you have an executable in your
.cabal file that starts up the server. Then in the
Procfile, you’ll specify that executable under the
Note though that you can’t use a hard-coded port! Heroku will choose a port for you. You can get it by retrieving the
PORT environment variable. Here’s what your code might look like:
runServer :: IO ()
runServer = do
port <- read <$> getEnv “PORT”
Run port (serve myAPI myServer)
Now you’ll need to “scale” the application to make sure it has at least a single machine to run on. From your repository, run the command:
heroku ps:scale web=1
Finally, we need to push our application to the Heroku container. To do this, make sure Heroku added the remote
heroku Github repository. You can do this with the following command:
git remote -v
It should show you two remotes named
heroku, one for fetch, and one for push. If those don’t exist, you can add them like so:
heroku git:remote -a haskell-test-app
Then you can finish up by running this command:
git push heroku master
You should see terminal output indicating that Heroku recognizes your application. If you wait long enough, you’ll start to see the Stack build process. If you have any environment variables for your project, set them from the app dashboard. You can also set variables with the following command:
heroku config:set VAR_NAME=var_value
Once our app finishes building, you can visit the URL Heroku gives you. It should look like
https://your-app.herokuapp.com. You’ve now deployed your Haskell code to the cloud!
There are a few weaknesses to this system. The main one is that our entire build process takes place on the cloud. This might seem like an advantage, and it has its perks. Haskell applications can take a LONG time to compile though. This is especially true if the project is large and involves Template Haskell. Services like Heroku often have timeouts on their build process. So if compilation takes too long, the build will fail. Luckily, the containers will cache previous results. This means Stack won’t have to keep re-downloading all the libraries. So even if our first build times out, the second might succeed.
This concludes part 1 of our Haskell Deployment series. We’ll see the same themes quite a bit throughout this series. It’s definitely possible to deploy our Haskell code using common services. But we often have to do a little bit more work to do so. Next week, we’ll see how we can automate our deployment process by incorporating Circle CI.
Want some more tips on developing web applications with Haskell? Download our Production Checklist to learn about some other libraries you can use! For a more detailed explanation of one approach, read our Haskell Web Skills series.
Create your free account to unlock your custom reading experience.