Hot-reloading on a dockerized flask app
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
Setting up the flask app
First, let’s create a directory to house the code.
And add flask to the
Now let’s make the actual
Check that the app is working.
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.
* Serving Flask app "app"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
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
We start with the base Python 3 image.
Create and designate
/app as the work directory.
RUN mkdir /app
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
COPY requirements.txt .
Install the requirements.
RUN pip install -r requirements.txt
Copy the rest of the files.
COPY . .
uwsgi with a config file.
CMD [ "uwsgi", "--ini", "app.ini" ]
Create the app.ini config for uwsgi
Add uwsgi to the
Create the dockerfile for Nginx
Start with the
Expose port 80 for
Replace the default config with a custom config
RUN rm /etc/nginx/conf.d/default.confCOPY app.conf /etc/nginx/conf.d
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 (
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
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
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) 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
-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.
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
make start, and stop it with
make stop (after terminating the
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!
Creation of dockerized flask app based on this article