Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Develop with Flask and Python 3 in a container on Red Hat Enterprise Linux

 

September 12, 2019
Rob Terzi
Related topics:
ContainersLinuxPython
Related products:
Red Hat Enterprise Linux

Share:

    In my previous article, Run Red Hat Enterprise Linux 8 in a container on RHEL 7, I showed how to start developing with the latest versions of languages, databases, and web servers available with Red Hat Enterprise Linux 8 even if you are still running RHEL 7. In this article, I’ll build on that base to show how to get started with the Flask microframework using the current RHEL 8 application stream version of Python 3.

    From my perspective, using Red Hat Enterprise Linux 8 application streams in containers is preferable to using software collections on RHEL 7. While you need to get comfortable with containers, all of the software installs in the locations you’d expect. There is no need to use scl commands to manage the selected software versions. Instead, each container gets an isolated user space. You don’t have to worry about conflicting versions.

    In this article, you’ll create a Red Hat Enterprise Linux 8 Django container with Buildah and run it with Podman. The code will be stored on your local machine and mapped into the container when it runs. You’ll be able to edit the code on your local machine as you would any other application. Since it is mapped via a volume mount, the changes you make to the code will be immediately visible from the container, which is convenient for dynamic languages that don’t need to be compiled. While this approach isn’t the way to do things for production, you get the same development inner loop as you’d have when developing locally without containers. The article also shows how to use Buildah to build a production image with your completed application.

    Additionally, you’ll set up the Red Hat Enterprise Linux 8 PostgreSQL application stream in a container that is managed by systemd. You can use systemctl to start and stop the container just as you would for a non-container installation.

    Install Podman and Buildah on Red Hat Enterprise Linux 7

    First, we need to install Podman, which is in the extras repo on Red Hat Enterprise Linux 7. The extras repo isn’t enabled by default. Developers should also enable the rhscl (Red Hat Software Collections), devtools, and optional repos:

    $ sudo subscription-manager repos --enable rhel-7-server-extras-rpms \
        --enable rhel-7-server-optional-rpms \
        --enable rhel-server-rhscl-7-rpms \
        --enable rhel-7-server-devtools-rpms

    Now install Podman and Buildah. If sudo isn’t set up on your system, see How to enable sudo on Red Hat Enterprise Linux.

    $ sudo yum install podman buildah

    Later, we’ll run containers with systemd. If SELinux is enabled on your system (it is by default), you must turn on the container_manage_cgroup boolean to run containers with systemd:

    $ sudo setsebool -P container_manage_cgroup on

    For more information, see the containers running systemd solution.

    Note: The Red Hat ID created when you joined Red Hat Developer gives you access to content on the Red Hat Customer Portal.

    Set up a Flask example app

    We need Flask code to run. Let’s use Flaskr, the sample app in the Flask distribution's examples/tutorial directory. Download Flask into a working directory on the host machine and extract the tutorial app:

    $ sudo mkdir /opt/src
    $ sudo chown $USER:$USER /opt/src
    $ cd /opt/src
    $ mkdir flask-app
    $ curl -L https://github.com/pallets/flask/archive/1.1.1.tar.gz | tar xvzf - 
    $ cp -pr flask-1.1.1/examples/tutorial flask-app

    We’ve now got an example Flask app at /opt/src/flask-app.

    Run Python 3.6 and Flask in a Red Hat Enterprise Linux 8 container (manually)

    Now we need Python 3.6 and Flask. We’ll manually set up a container with the dependencies and then run the app to see how it’s done. Let's start with the Red Hat Enterprise Linux 8 Universal Base Image (UBI). If you're unfamiliar with the RHEL UBIs, see the section "Red Hat Universal Base Images."

    Red Hat has a new container registry which uses authentication: registry.redhat.io. A Red Hat account isn’t required to use UBI images, but other Red Hat images that aren’t part of UBI can only be obtained through registry.redhat.io. The Red Hat ID created when you joined Red Hat Developer gives you access to the Red Hat Container Registry, so for simplicity, I use only registry.redhat.io in this example.

    If you aren’t logged in when you try to pull an image, you’ll get a verbose error message:

    ...unable to retrieve auth token: invalid username/password.

    Log in with your Red Hat username and password:

    $ sudo podman login registry.redhat.io

    Note: Podman was designed to run without root. However, the support for this feature isn’t there with Red Hat Enterprise Linux 7.6. For more information, see Scott McCarty’s, A preview of running containers without root in RHEL 7.6.

    Now run the container, making our source directory /opt/src available inside the container and exposing port 5000 so you can connect to the Flask app with a browser on the host system:

    $ sudo podman run -v /opt/src:/opt/src:Z -it -p 5000:5000 registry.redhat.io/ubi8/ubi /bin/bash

    The previous command also invoked an interactive shell for the Red Hat Enterprise Linux 8 based UBI container. From inside the container, see what application streams are available with RHEL 8:

    # yum module list

    You might notice an extra group of application streams labeled Universal Base Image. See the UBI section for more information about Red Hat Universal Base Images.

    Next, install Python 3.6:

    # yum -y module install python36

    Python 3.6 is now installed in our container and is in our path as python3, not python. If you want to know why see Petr Viktorin’s article, Python in RHEL 8.

    Next, use pip to install Flask:

    # pip3 install flask

    You’ll get a warning about running pip as root. Running pip as root on a real system is generally a bad idea. However, we’re running in a dedicated container which is isolated and disposable, so we can do pretty much whatever we want with files in /usr.

    Let’s check where the Flask command-line interface (CLI) was installed:

    # which flask

    Pip installed it into /usr/local/bin.

    Now let’s run the example app inside of the container:

    # cd /opt/src/flask-app
    # export FLASK_APP=flaskr
    # export FLASK_ENV=development
    # flask init-db
    # flask run --host=0.0.0.0

    Using a browser on the host system, go to http://localhost:5000/ and view the resulting page:

    Now, you’ve got a container configured by hand that runs Flask applications using Red Hat Enterprise Linux 8’s Python 3.6 application stream on your RHEL 7 system. You could treat this container like a "pet," and use podman restart -l and podman attach -l when you want to run it again—as long as you don’t delete it. We didn’t name the container, but the -l conveniently selects the last running container. Alternatively, you’d need to use podman ps -a to get the ID, or randomly generated name to pass to podman restart and podman attach.

    When you restart the container, it is similar to rebooting a system. The installed files are there, but any of the other runtime state-like environment variable settings won't persist. The life cycle for containers you’ve seen in most tutorials is "run then delete" since containers are designed to be ephemeral. However, knowing how to create and restart containers can be handy when you need to experiment.

    Create a Flask container image with Buildah

    To make things easier, we’ll create a container image that has Flask installed and starts the Flask app anytime the container is run. The container won’t have a copy of the app, we’ll still map the app into the container from the host system. The code will be stored on your local machine where you can edit it as you would any other application source. Because it is mapped via a volume mount, the changes you make to the code will be immediately visible inside the container.

    When creating images with Buildah, you can use Dockerfiles or Buildah command lines. For this article, we’ll use the Dockerfile approach because you’ve probably seen it before in other tutorials.

    Because we are working with files that are shared between your host system and the container, we’ll run the container using the same numeric user ID (UID) as your regular account. While inside the container, any files are created in the source directory are owned by your user ID on the host system. Find out your UID with the id command:

    $ id

    Make a note of the number after UID= and GID= at the start of the line. On my system, my UID and GID are both 1000. In the Dockerfile and other examples here, change the USER line to match your UID:GID.

    In /opt/src/flask-app, create Dockerfile with the following contents:

    FROM registry.redhat.io/ubi8/python-36
    
    RUN pip3 install flask
    
    # set default flask app and environment
    ENV FLASK_APP flaskr
    ENV FLASK_ENV development
    
    # This is primarily a reminder that we need access to port 5000
    EXPOSE 5000
    
    # Change this to UID that matches your username on the host
    # Note: RUN commands before this line will execute as root in the container
    # RUN commands after will execute under this non-privileged UID
    USER 1000
    
    # Default cmd when container is started
    # Create the database if it doesn't exist, then run the app
    # Use --host to make Flask listen on all networks inside the container
    CMD [ -f ../var/flaskr-instance/flaskr.sqlite ] || flask init-db ; flask run --host=0.0.0.0
    

    A note on the Dockerfile: Instead of installing Python 3.6, I used a UBI image from Red Hat that already had Python 3.6 on top of the UBI 8 image. The command that runs when the container starts will create the database if it doesn’t exist, and then run the Flask app.

    Next, build the Flask container (don’t forget the trailing .):

    $ sudo buildah bud -t myorg/myflaskapp .

    Now we can run the Flask container containing our app:

    $ sudo podman run --rm -it -p 5000:5000 -v /opt/src/flask-app:/opt/app-root/src:Z myorg/myflaskapp

    The Flaskr app should now be running, which you can verify by using a browser on the host system and going to http://localhost:8000/ to view the resulting page.

    You can now edit the code in /opt/src/flask-app like you would any regular source code. When you need to restart Flask, Ctrl+C the container. Note the --rm in the run command, which automatically removes the container when it exits.

    To start the container again, you will need to use the above podman run command again, which creates a fresh new container, plus a new database with nothing in it. For many situations, this fresh start is desirable.

    Persist the SQLite database between containers

    The Flaskr example uses a SQLite database, which is stored inside the container. Containers are intended to be ephemeral, so any changes made inside the container will be lost when the container is deleted.

    There are several ways you can keep the database (or other files) from containers across runs. As mentioned above, you could try to keep the container around and restart it, instead of recreating it with run every time. While that practice can be handy for experimenting and debugging, this isn’t a good way to accomplish persistence. Now is a good time to mention if you do have changed files you’d like to get out of a container that has exited but hasn’t been removed, Podman and Buildah have a handy mount command that mounts the container on the host system so you can access the files through the filesystem.

    Note: If you are confused about the difference between a container and a container image, see Scott McCarty’s article: A Practical Introduction to Container Terminology.

    Instead of trying to keep the container around, a much cleaner solution is to arrange for the database (or other files you’d like to persist) to be stored in the host's filesystem. You can do this by adding another volume mount with -v to the run command. Here’s the full command, which stores the database with the source code:

    $ sudo podman run --rm -it -p 5000:5000 -v /opt/src/flask-app:/opt/app-root/src:Z \
        -v /opt/src/flask-app/instance:/opt/app-root/var/flaskr-instance:Z myorg/myflaskapp

    Run MariaDB in a container

    Another way to deal with persistence is to run a database server in another container. In a previous article, Run Red Hat Enterprise Linux 8 in a container on RHEL 7, I showed how to run MariaDB using the current Red Hat Enterprise Linux 8 application stream on a RHEL 7 system. The MariaDB container is managed by systemd, so you can use systemctl commands just like you would for a non-containerized version.

    For the sake of brevity, I won’t replicate the instructions to get MariaDB running in this article, just follow the previous article's MariaDB section to get that database running.

    The one thing you’ll need to know is how to make your Flask container connect to the database container. By default, containers are designed to run with an isolated virtual network. Steps need to be taken to network containers together. I think the easiest approach for the scenario in this article—where you just want to run a few containers—is to arrange for the containers to share the host’s network.

    To use the host's network, add --net host to the run command for both your Flask and database containers. If you are using the host’s network, you won’t need to select which ports to expose. So, the full run command for the Flask container is:

    $ sudo podman run --rm -it --net host -v /opt/src/flask-app:/opt/app-root/src:Z \
        -v /opt/src/flask-app/instance:/opt/app-root/var/flaskr-instance:Z myorg/myflaskapp

    While using the host’s network is quick and easy for development, you’d run into port conflicts if you had a number of MariaDB containers that all wanted to use port 3306. One way to improve this setup is to use Podman’s pod capabilities to put the app and database containers in the same pod, where they share namespaces. See Brent Baude’s article, Podman: Managing pods and containers in a local container runtime.

    Use Buildah to create an image with your Flask app

    After you’ve developed your app, you can use Buildah to create a distributable container image with your Flask app. We’ll use Buildah command lines instead of a Dockerfile. This approach is much more flexible for complex builds and automation: You can use shell scripts or whatever other tools you use for your build environment.

    In /opt/src/flask-app, create app-image-build.sh with the following contents:

    #!/bin/sh
    # Build our Flask app and all the dependencies into a container image
    # Note: OOTB on RHEL 7.6 this needs to be run as root.
    
    MYIMAGE=myorg/myflaskapp
    FLASK_APP=flaskr
    FLASK_ENV=development
    USERID=1000
    
    IMAGEID=$(buildah from ubi8/python-36)
    buildah run $IMAGEID pip3 install flask
    
    buildah config --env FLASK_APP=$FLASK_APP --env FLASK_ENV=$FLASK_ENV $IMAGEID
    
    # any build steps above this line run as root inside the container
    # any steps after run as $USERID
    buildah config --user $USERID:$USERID $IMAGEID
    
    buildah copy $IMAGEID . /opt/app-root/src
    buildah config --cmd '/bin/sh run-app.sh' $IMAGEID
    
    buildah commit $IMAGEID $MYIMAGE
    

    This image calls a start script to launch our application. Next, create run-app.sh in the same directory, with the following contents:

    #!/bin/sh
    
    APP_DB_PATH=${APP_DB_PATH:-../var/instance/flaskr.sqlite}
    
    if [ ! -f ${APP_DB_PATH} ]; then
    echo Creating database
    flask init-db
    fi
    
    echo Running app $FLASK_APP
    flask run --host=0.0.0.0
    

    Now, build the image:

    $ sudo app-image-build.sh

    Run and test the new image:

    $ sudo podman run --rm -it --net host -v /opt/src/flask-app/instance:/opt/app-root/var/flaskr-instance:Z myorg/myflaskapp

    When you are ready, you can distribute your application by pushing it to a container registry like Red Hat’s Quay.io.

    Next steps

    By now, you should see that it is easy to get the software components you need running in containers so you can focus on development. It shouldn’t feel very different from developing without containers.

    The Flask container you built isn’t tied to a specific app. You could reuse that container for other Flask apps by overriding the environment variables: add -e FLASK_APP mynewapp to the podman run command.

    You could also build on the Dockerfile above to install more Python modules for your app into your container image, or customize the way the app starts.

    Check out what other UBI 8 images are available in the Red Hat Container Catalog. If the language, runtime, or server aren’t available as a UBI image, you can build your own beginning with the ubi8 base image. Then, you can add the application streams and other rpms you need with yum commands in a Dockerfile, or with buildah run.

    Red Hat Universal Base Images

    I’ve mentioned Universal Base Images (UBIs) a number of times in this article without explaining them. Red Hat provides these UBIs to use as a base for your container images. From Mike Guerette’s article, Red Hat Universal Base Image: How it works in 3 minutes or less:

    "Red Hat Universal Base Images (UBI) are OCI-compliant container base operating system images with complementary runtime languages and packages that are freely redistributable. Like previous RHEL base images, they are built from portions of Red Hat Enterprise Linux. UBI images can be obtained from the Red Hat Container Catalog and be built and deployed anywhere.

    "And, you don’t need to be a Red Hat customer to use or redistribute them. Really."

    With the release of Red Hat Enterprise Linux 8 in May, Red Hat announced that all RHEL 8 base images would be available under the new Universal Base Image End User License Agreement (EULA). This fact means that you can build and redistribute container images that use Red Hat’s UBI images as your base, instead of switching to images based on other distributions, like Alpine. In other words, you won’t have to switch from using yum to using apt-get when building containers.

    There are three base images for Red Hat Enterprise Linux 8. The standard one is called ubi, or more precisely, ubi8/ubi. This is the image used above which you will probably use most often. The other two are minimal containers. They contain little supporting software for when image size is a high priority and a multi-service image that allows you to run multiple processes inside the container managed by systemd.

    Note: There are also UBI images for Red Hat Enterprise Linux 7 under ubi7 if you want to build and distribute containers running on a RHEL 7 image. For this article, we’ll only use the ubi8 images.

    If you are just starting out with containers, you don't need to delve into UBI details right now. Just use the ubi8 images to build containers based off Red Hat Enterprise Linux 8. However, you will want to understand UBI details when you start distributing container images or have questions about support. For more information, see the references at the end of this article.

    More information

    Related articles:

    • Run Red Hat Enterprise Linux 8 in a container on RHEL 7 (covers PHP 7.2, MariaDB, and WordPress running in containers)
    • Setting up a Django application on RHEL 8 Beta

    Cheat sheets:

    • Podman Basics Cheat Sheet
    • Red Hat Enterprise Linux 8 Cheat Sheet

    Podman and Buildah:

    • Podman and Buildah for Docker users
    • Managing containerized system services with Podman
    • Podman: Managing pods and containers in a local container runtime
    • Getting started with Buildah
    • Building, Running, and Managing containers - RHEL 8 Documentation
    • Getting Started with Containers - RHEL 7 Documentation

    UBI: 

    • Red Hat Universal Base Image: How it works in 3 minutes or less
    • Red Hat Universal Base Images (UBI)
    • UBI FAQ
    Last updated: March 29, 2023

    Recent Posts

    • Expand Model-as-a-Service for secure enterprise AI

    • OpenShift LACP bonding performance expectations

    • Build container images in CI/CD with Tekton and Buildpacks

    • How to deploy OpenShift AI & Service Mesh 3 on one cluster

    • JVM tuning for Red Hat Data Grid on Red Hat OpenShift 4

    What’s up next?

     

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue