When I was setting up the CI/CD for one of my side projects I built the docker images to allow using the same image for different purposes. This is useful in Python projects if need to run different processes from the same code base. For example, running the Django web worker and also a Celery worker for background task processing.

Here’s an excerpt from the Dockerfile:

COPY ./compose/production/django/entrypoint /entrypoint
COPY ./compose/production/django/start /start
COPY ./compose/production/django/celery/worker/start /start-celeryworker
ENTRYPOINT ["/entrypoint"]

When releasing a Docker image the Herok documentation shows how to use the formation API using separate Docker images. Instead of using separate images you can use the same image and supply different commands for each formation as follows:

curl --netrc -X PATCH https://api.heroku.com/apps/$APP_ID_OR_NAME/formation \
  -d '{
  "updates": [
    {
      "type": "web",
      "docker_image": "$DOCKER_IMAGE_ID",
      "command": "/start"
    },
    {
      "type": "worker",
      "docker_image": "$DOCKER_IMAGE_ID",
      "command": "/start-celeryworker"
    }
  ]
}' \
  -H "Content-Type: application/json" \
  -H "Accept: application/vnd.heroku+json; version=3.docker-releases"

Full details of the Formation API can be found in the Heroku API docs.