← blog

Deploying a Flask App On a Raspberry Pi with Docker

I've been interested in learning Docker for quite some time, but I never really found a need to. So for my personal project I'm tying a lot of different services as a vehicle to learn. I'm also dusting off my Raspberry Pi 4 since I'm too cheap to pay for a cloud provider, but I want to practice working on a distributed system.

A little about my setup

I'm using a Raspberry Pi 4b 4g version. Its pretty sweet, but I haven't really used it to its full potential. I last used it for Bridge, a community router. I've been itching to get started a home lab though. But in the mean time I need to get more practice with working with some of the tools. I decided to use my Raspberry Pi as my server which will host my project.

After a bit of playing around, I decided on using an Arch ARM 64 for my operating system. I have made instructions for setting up Arch on the Raspberry Pi, which you can check out. After setting up the Pi, you can install docker in the Pi's terminal with pacman -S docker. To enable IP forward you can run echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.d/40-ip-forward.conf.

I decided to change the location where Docker images are saved. Typically they are saved in /var/lib/docker, but I want them in /mnt/docker. To do this you copy the folder over using cp -r /var/lib/docker/ /mnt/docker. Then you write to /etc/docker/daemon.json:

{
  "data-root": "/mnt/docker"
}

Finally, you can start the docker service with: systemctl start docker.service

Something to know about using Docker on Raspberry Pi, is that the Pi runs on an ARM chip. So, when browsing on docker hubs for pre-built containers you might have errors when running them. This is because they were not compiled for ARM. It's possible to cross compile a container, but easier is to look for containers tagged RPi or ARM.

Flask App

Lately, I've been on a python kick, and wanting to do everything in python (or golang ;)) So I've been going over Flask a bit, and it has been pretty fun. Anyways, for a quick docker test, I wanted to deploy a flask web app via docker.

Without going too far into the weeds about flask, I developed it in a virtual environment and it only serves a page. My flask app structure starts like like:

myvenv/
app/
  /__init__.py
  /routes.py
app.py

The files were simple and looked like:

# app.py
import os
from app import app

if __name__ == "__main__":
  app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))

---

# __init__.py
from flask import Flask

app = Flask(__name__)

from app import routes

---

# routes.py
from app import app

@app.route('/')
@app.route('/index')
def index():
    return "Hi!"

Dockerizing the app

When developing the app, I'm typically working in a virtual environment in order to silo my project. Some research into Docker and virtualenv found that using a virtualenv was a better practice as it further controls the environment of a python application, while Docker will package OS-level dependencies. Further, if you are developing in a virtual environment it is better to deploy it that way to reduce any differences between your development and containerized setup. I guess the only thing is that it is a little different to activate the virtual environment.

First, to prepare the project for deployment you must collect the dependencies for the app. You can run pip > requirements.txt. This will output a text file that tells which packages you use, and their specific versions. This makes it easy to replicate a project.

Next, must create a dockerfile which provides docker the instructions to build a container. To do this first make a file called Dockerfile.

Then inside docker file you'll need to have:

FROM python:3

ENV VIRTUAL_ENV=/opt/venv
RUN python -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
COPY . .

COPY requirements.txt .

RUN pip install --upgrade-pip
RUN pip install -r requirements.txt

EXPOSE 5000

COPY app.py .
CMD ["python", "./app.py"]

The virtual environment is now used when you run the container.

Now to build the docker container you can run: docker -t flasktest .

There you have it. You've built your first docker container. The command will step through your dockerfile and install a python container if you don't already have it, then create a virtual environment, installs the dependencies, and exposes port 5000.

Now to run the container you can run: docker run -p 8888:5000 flasktest. This will run the container and bind it to port 8888.

To view the page, on another computer on the same network as the pi, use a browser and go to <pi_ipaddr>:8888, and you will see the app.

So this small demo shows a few things, how to set up Docker on a Raspberry Pi, a basic flask app, and how to deploy it with docker.

The project can be found on github