Creating a "Deploy to Heroku" button

2 minute read

Heroku makes it a breeze to deploy applications to the cloud on their free instances. Although it’s straightforward, here are my notes of how I set up the “Deploy to Heroku” button for No Cookie Analytics.

Step 1: Choose a Docker image

The No Cookie Analytics comprises separate backend and frontend (SPA) apps. To make the deployment process less complex, I set up a monolith Docker image where the backend served static assets.

Step 2: Define app.json

My app.json looked like this. Some notes:

  • Environment keys are either constants or are generated by Heroku. Generated keys are perfect for secrets.
  • Constant field values are user-configurable in the deploy template page.
  • Post-deploy scripts can run database migrations.
  "name": "No Cookie Analytics",
  "description": "Hassle-free privacy-friendly analytics",
  "repository": "",
  "logo": "",
  "keywords": ["python", "analytics"],
  "website": "",
  "env": {
    "SECRET_KEY": {
      "description": "A secret key for verifying the integrity of signed cookies.",
      "generator": "secret"
      "description": "The first superuser to be created",
      "value": ""
      "description": "The password for the first superuser",
      "value": "something-very-secure-please-change"
  "stack": "container",
  "success_url": "/login",
  "scripts": {
    "postdeploy": "sh /app/"
  "addons": [
      "plan": "heroku-postgresql"

Step 3: Define heroku.yml

It’s confusing that the “Deploy to Heroku” button needs two files to work, but this one is simple and it’s a pointer to the Dockerfile.

    web: Dockerfile


The “Deploy to Heroku” button can now be embedded in the README. I chose the easier route, where Heroku reads the referrer to find out the Github repository to deploy from:


 <!-- Alternative, explicit template -->


Step 4: Speed things up

Everything already works, but I noticed that Heroku was rebuilding the image for every single deploy. This was taking about 5 minutes to build the monolith Docker image. To make this process faster, I set up a Github Actions workflow to publish the monolith Docker image to the Github Container Registry. Then I added a one-line Dockerfile.heroku to my project:


# This dockerfile is just a reference to the pre-built docker image on ghcr,
# so Heroku does not have to build the image every time.

And I updated heroku.yml like this:

    web: Dockerfile.heroku