Hot-reloading on a dockerized flask app

Michael Xian
Aug 17, 2020 · 4 min read

At Hootsuite, my team was working with a dockerized flask app. Because we hadn’t set up hot-reloading, when we were making updates to the flask app and testing locally, we had to manually tear down the containers and bring them back up between each change. This took some time, and was mildly disruptive to our workflow, so I worked on setting up hot-reloading.

Table of Contents

Prerequisites

Setting up the flask app

First, let’s create a directory to house the code.

Create a requirements.txt file.

And add flask to the requirements.txt file

Now let’s make the actual flask app.

Check that the app is working.

We export FLASK_APP to tell flask where to find our application. flask run has to be run in the same terminal as export FLASK_APP=app.py is run.

Example output:

Visit the site, and you should see something like this:

Creating the dockerized flask app

If you skipped the previous step, you can use this starter code

Create the dockerfile for our flask app

We’ll have a separate dockerfile for Nginx, so let’s call this Dockerfile-flask.

We start with the base Python 3 image.

Create and designate /app as the work directory.

Expose the port for our app.

Because of how docker caches, it’s best to first copy the files that we don’t expect to change often. For this project, that’s the requirements.txt file.

Install the requirements.

Copy the rest of the files.

Run uwsgi with a config file.

Create the app.ini config for uwsgi

Add uwsgi to the requirements.txt file

Create the dockerfile for Nginx

Start with the Nginx image.

Expose port 80 for Nginx.

Replace the default config with a custom config

Create the app.conf for Nginx

Create the docker-compose yaml

image: defines the name of the image, which can be anything we want

context: tells docker where to look for files to build the image

dockerfile: defines the dockerfile to use, which is just the dockerfiles we created in the previous steps.

This mounts the root directory of our project on the host machine (./) onto a directory named app under the root directory in the container (/app).

This maps port 5000 on our machine to port 80 on the Nginx container. This is important because Nginx listens on port 80 by default, but our app is running on port 5000.

You should now be able to reach the app on localhost:5000 after running docker-compose up

Adding hot-reloading

If you skipped the previous steps, you can use this starter code

uwsgi has a touch-reload configuration which allows us to designate a file to cause an update whenever the touch command is applied on it. However, we can only supply one file, so we use watchman to detect updates in our python files, and run touch <FILE> to trigger the uwsgi reload. I’ll be using a dedicated file named uwsgi-reload. To do this, add touch-reload = uwsgi-reload to the app.ini file.

To check for updates to our python files, we can run watchman-make -p '**/*.py' -s 1 — run ‘touch uwsgi-reload’.

-p '**/*.py' specifies the pattern for which files to watch for changes. Specifically, we’re looking for any file that ends in .py (*.py) at any level (**/) in our project.

-run 'touch uwsgi-reload' tells watchman to run touch uwsgi-reload whenever it detects an update. This will then trigger uwsgi to reload, since it’s watching the uwsgi-reload file.

-s 1 tells watchman to wait until there is 1 second without any updates before running touch uwsgi-reload. This essentially means that it will wait for you to stop typing for 1 second before triggering the reload, so it’s not constantly triggering reloads as you’re typing.

Now, if we run the watchman-make command after docker-compose up, the project will reload whenever you make an update to a .py file. We can use make to run this automatically instead.

We run docker-compose up with the -d flag so it runs in a detached state. This allows the terminal to hit the watchman-make command without shutting down the containers.

With this makefile, we can start the project with hot-reloading with make start, and stop it with make stop (after terminating the watchman-make command).

Now if you run make start, you should be able to see your changes in real time. Try changing the 'Hello World!' to something else!

Finalized hot-reloading dockerized flask app

Creation of dockerized flask app based on this article

Hootsuite Engineering

Hootsuite's Engineering Blog