Creating a "Deploy to Heroku" button
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": "https://github.com/nocookie-analytics/core",
"logo": "https://nocookieanalytics.com/img/logo.png",
"keywords": ["python", "analytics"],
"website": "https://nocookieanalytics.com",
"env": {
"SECRET_KEY": {
"description": "A secret key for verifying the integrity of signed cookies.",
"generator": "secret"
},
"FIRST_SUPERUSER": {
"description": "The first superuser to be created",
"value": "admin@example.com"
},
"FIRST_SUPERUSER_PASSWORD": {
"description": "The password for the first superuser",
"value": "something-very-secure-please-change"
}
},
"stack": "container",
"success_url": "/login",
"scripts": {
"postdeploy": "sh /app/prestart.sh"
},
"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.
build:
docker:
web: Dockerfile
Result #
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:
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
<!-- Alternative, explicit template -->
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://www.heroku.com/deploy?template=https://github.com/<USER>/core/tree/<BRANCH>)
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:
FROM ghcr.io/nocookie-analytics/nocookie-analytics:main
# 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:
build:
docker:
web: Dockerfile.heroku