Deploy a Flask app on Heroku using Docker
Written while containerising a Detectron2-backed deep learning project of mine. Every tutorial I found went either far too deep or stopped halfway, so this is the whole path in one place.
Dockerising a Flask app before it goes to Heroku buys you one thing worth having: you stop being limited to what Heroku offers. The image runs anywhere. Moving to another cloud provider becomes a deploy, not a rewrite.
If you’ve never touched Docker, that’s fine — everything you need is below.
Why Docker
Containerising is a good way to put a machine learning model into production. Docker uses OS-level virtualisation to ship software as containers: the model, its Python version, its system libraries and its weights all travel together, so the thing you tested is the thing that runs.
Image versus container
An image is the software package you build from your project. A container is a running instance of that image, and one image can have many containers. The distinction is subtle enough that plenty of people use the words interchangeably — it’s worth getting right before you start reading error messages.
The Dockerfile
A Dockerfile is an extensionless text file telling the Docker daemon how to build your image.
FROM— the base image. Here, the official Python 3.7 image. You can build on top of anything.RUN— runs a command at build time, baking the result into the image.EXPOSE— tells the daemon to make port 5000 available for external mapping. If your app serves on 5000, this is the line.ENTRYPOINTandCMD— what actually runs. In this case,python app.py.
Installing Docker
Register an account, then install Docker Desktop from the official docs. On Windows you’ll need 10 version 1903 (May 2019) or Enterprise. Start the app once before you go any further.
Building the image
Your project root needs three files:
Dockerfilerequirements.txtapp.py
In app.py, guard the entry point with
if __name__ == "__main__" and — this is the part people miss — set
the host explicitly:
app.run(host="0.0.0.0", port=5000)
Without the host, the server binds to localhost inside the container and nothing outside it can connect. The app will look broken; it isn’t.
From the project root:
docker build --tag flask-demo-app .
Then run it, mapping your port 5000 to the container’s:
docker run -d -p 5000:5000 flask-demo-app
Open localhost:5000 and the app should be up.
docker ps lists what’s running — you’ll need the container ID from
there to stop it.
Deploying to Heroku
- Install the Heroku CLI via your package manager or the
binary,
and check it with
heroku --version. heroku login, thenheroku container:loginto register your container.
Create the app, push the image, release it:
heroku create demo-app
heroku container:push web --app demo-app
heroku container:release web --app demo-app
That’s it — the app is in production. Worth reading next: dynos, the abstraction that keeps you out of infrastructure management, and the CLI’s log and repository commands, which are where you’ll live the first time something misbehaves.
Found this useful, or found a gap in it? Tell me.
All writing