Deploying Clojure Apps on Heroku
Last updated October 16, 2024
Table of Contents
This article describes how to take an existing Clojure app and deploy it to Heroku.
If you’re new to Heroku, start with the Getting Started with Clojure on Heroku tutorial.
Overview
To generate a project skeleton that has some extra convenient features that are useful for Heroku development, type lein new heroku helloworld
. The skeleton project’s README.md file describes what it contains and how to use it.
The most important parts of an app are: your application’s source code in the src
directory, a project.clj
file in the root directory, and a Procfile that defines the app’s process types. A database isn’t automatically provisioned for Clojure apps, but it’s easy to add one if you need it.
Your Clojure application’s Source Code
Put your application source code into a folder within the src
directory.
The project.clj
File
Clojure’s dependency manager uses the project.clj
file. Heroku’s Clojure support is applied only if a project.clj
file exists in the root directory.
Projects that depend on dependencies that aren’t available in public repositories can deploy them to private repositories. The easiest way to do this is using a private S3 bucket with the s3-wagon-private Leiningen plugin.
It’s highly recommended to use AOT (ahead-of-time compilation) during deployment because it speeds up app boot time and catches certain types of errors before the app is live. The following project.clj
settings apply AOT during deployment but not during local development:
:profiles {:uberjar {:aot :all}}
The Procfile
A Procfile is a text file in the root directory of your application that defines process types and that explicitly declares what command must be executed to start your app. It looks something like this:
web: java $JVM_OPTS -cp target/helloworld-standalone.jar clojure.main -m helloworld.web
This declares a single process type, web, and the command needed to run it. The name web is important here. It declares that this process type will attach to the HTTP routing stack of Heroku, and receive web traffic when deployed.
The command in a web process type must bind to the port number specified in the PORT
environment variable. If it doesn’t, the dyno doesn’t start.
Procfiles can contain additional process types. For example, you can declare one for a background worker process that processes items off a queue.
Your app’s config is exported to the command in the Procfile as environment variables. For instance, running heroku config:add JVM_OPTS=...
changes the value used here.
How to Keep Build Artifacts Out of git
Prevent build artifacts from going into revision control by creating a .gitignore
file. Here’s a typical .gitignore
:
/target
/pom.xml
/.lein-*
/.env
Console
Heroku allows you to run one-off dynos, scripts and applications that only execute when needed, by using the heroku run
command. Use this command to launch a REPL process attached to your local terminal for experimenting in your app’s environment:
Running heroku run lein repl
uses a simplified version of the repl
task provided by Leiningen.
$ heroku run lein repl
Running lein repl attached to terminal... up, run.1
Downloading Leiningen to .lein/leiningen-2.2.0-standalone.jar now...
[...]
Clojure 1.5.1
user=>
Since Leiningen isn’t included in your slug for size reasons, it’s downloaded on-demand here. The repl has your app’s namespaces available. For example:
user=> (require 'hello.world)
nil
user=> (hello.world/app {})
{:status 200, :headers {"Content-Type" "text/plain"}, :body "Hello, world"}
One-off Scripts
You can run a one-off Clojure script attached to the terminal in the same way, as long as the script exists in your deployed app. Try making a small script that prints to the console and exits:
src/hello/hi.clj
(ns hello.hi)
(defn -main [& args]
(println "Hello there"))
Run the script in a one-off dyno with heroku run
:
$ heroku run lein run -m hello.hi
Running lein run -m hello.hi attached to terminal... up, run.2
Hello there