Programmatically release code to Heroku

Currently in beta, the Heroku Platform API lets developers automate, extend and combine the Heroku platform with other services in a programmatic, self-service way. Today we are setting the capstone into the API by adding slug and release endpoints to the API beta.

These API endpoints are special. They expose a very core part of what Heroku does best: Quickly and safely releasing new versions of an app onto the Heroku platform.

Using the new slug and release endpoints, platform developers can build integrations and services that completely sidestep the traditional Heroku Git deployment flow. So instead of requiring git push heroku master to deploy, it’s now possible to do things like:

  • Write a script that builds an app in a local directory on your machine, tars it up, and deploys it directly to a Heroku app
  • Extend your favorite continuous integration server or service to deploy directly to Heroku if unit tests pass – no need to use Git or manage SSH keys
  • Move slugs around between Heroku apps, for example to promote a release on a staging app to a production app

Are you a CI service provider, source code repository provider, or just a plain old hacker interested in the opportunities this opens up? We want to work with you, get in touch with

The slug and release endpoints complete the functionality that we plan to expose in the first version of the Heroku Platform API. The API is still in beta and we welcome questions and feedback in the Heroku API Forum.

Slugs and Releases

Platform API

“Slug” refers to one of two things:

  1. A tarballed bundle of source code, fetched dependencies and compiled or generated output of a build system, ready for execution on the Heroku runtime
  2. A record created in the Heroku database when you register and upload a slug tarball using the slug endpoint on the platform API

The slug API endpoint allows slug records to be created and read. It contains attributes including the URL of the slug tarball, the process types that the slug can run (mirroring the contents of the Procfile), and a version control identifier (typically the SHA from the Git commit of the source code used to create the slug)

Releases are versioned deployments of an app, containing a slug and a set of config vars. The release API endpoint allows releases to be created and read. Releases require a slug and can optionally contain a description.

Slugs can be shared amongst releases – by fetching the slug from an existing release on any given app, a new release can be created on any other app using the existing slug. This allows slugs to be quickly and efficiently promoted between environments (such as from staging to production), and the release endpoint can be used to programmatically create custom deployment workflows.

The rest of this post contains two examples that demonstrate the power of the new API endpoints. The first shows how to move slugs between apps, the second demonstrates how to create and deploy slugs from scratch, from your local machine.

Full documentation is available on Dev Center in these articles:

Copying slugs

There are several ways to get a valid slug that can be released to an app. The simplest method is to copy a slug generated by Heroku for a different app. Imagine that you have pushed code to a staging app, the code has been tested and you are now ready to release to production. Instead of pushing to the production app and waiting for the code to build again, you can simply copy the slug from the staging app to the production app.

First, list releases on the staging app to get the id of the slug to release:

$ curl -H "Accept: application/vnd.heroku+json; version=3" -n \
"slug":{ "id":"ff40c84f-a538-4b65-a838-88fdd5245f4b" }

Now, create a new release on the production app using the slug from the staging app:

$ curl -X POST -H "Accept: application/vnd.heroku+json; version=3" -n \
-H "Content-Type: application/json" \
-d '{"slug": "ff40c84f-a538-4b65-a838-88fdd5245f4b"}' \

That’s it! The new code is now running on the example-app-production app.

Note that copying slugs between apps is already possible using the beta Pipelines plugin, and Heroku Fork uses a similar mechanism when forking an app. The slug and releases endpoints expose the primitives necessary for third-party API developers to build services offering similar functionality and much more.

Creating slugs from scratch

Slugs don’t have to be generated by Heroku: You can use the releases endpoint to deploy anything the platform recognizes as a valid slug. Let’s create a slug containing a simple Node.js app and the dependencies required to run it on Heroku. On your local machine, create a folder named app to hold the slug and fetch the Node.js runtime:

$ mkdir app 
$ cd app
$ curl | tar xzv

Add the app code in a file named web.js:

// Load the http module to create an http server
var http = require('http');

// Configure HTTP server to respond with `Hello World` to all requests
var server = http.createServer(function (request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end("Hello World\n");

var port = process.env.PORT;

// Listen on assigned port

// Put a friendly message on the terminal
console.log("Server listening on port " + port);

Tar up the slug:

$ cd ..
$ tar czfv slug.tgz ./app

The above process of creating a slug is a very simplified variant of what buildpacks do when normal slugs are created on Heroku: Make sure the source code is placed where it’s supposed to be and package any dependencies (the Node.js runtime in this case) into the slug.

The slug is now ready for release. This is a three-step process:

  1. Create a unique id and URL for the slug using the new slug endpoint
  2. Upload the slug
  3. Release the slug on the app

First, we register the new slug using the API. We have to keep track of the “put” url and the id that Heroku returns:

$ curl -X POST -H 'Content-Type: application/json' \
-H 'Accept: application/vnd.heroku+json; version=3' \
-d '{"process_types":{"web":"node-v0.10.20-linux-x64/bin/node web.js"}}' \
    "method": "put",
    "url": ""

Notice that we pass a “process_types” parameter. This has content similar to what is included in the Procfile that is typically included in apps pushed to Heroku. Passing process_types is necessary because the Procfile is not parsed when slugs are launched in dynos. Instead, the Procfile is parsed by the build system and the contents are passed in when the slug is created as demonstrated above.

Use curl to upload the slug to the URL provided by Heroku:

$ curl -X PUT -H "Content-Type:" --data-binary @slug.tgz "…"

Finally, release the slug to the app example-app:

$ curl -X POST -H "Accept: application/vnd.heroku+json; version=3" \
-H "Content-Type: application/json" \
-d '{"slug":"d969e0b3-9892-3113-7653-1aa1d1108bc3"}' \

We can then check that the release was created:

$ heroku releases --app example-app
=== example-app Releases
v3  deploy  2013/10/08 16:09:54 (~ 1m ago)

To verify that the slug was deployed, run$ heroku open --app example-app. Please see the full example on Dev Center for additional details on how to create slugs. The Dev Center article has both the Node.js example above and examples demonstrating how to create simple slugs with Ruby and Go apps.


With the releases and slugs endpoints, the primitives that power Heroku features like Pipelines and Fork are now available to developers using the Platform API. This opens up a lot of exciting possibilities for partners and hackers to build and innovate on top of Heroku. We will be working with partners to build integrations as we take the API to general availability. Get in touch with if you are interested in participating.

The release and slug endpoints are in public beta with the rest of the Platform API. Once we’re confident the API is free of bugs, we will freeze the design and release it into GA. Until that time, we may introduce breaking changes to the API. All changes will be posted in the Heroku Changelog.

If you have any questions or feedback, please start a discussion in the Heroku API Forum.

Browse the blog archives or subscribe to the full-text feed.