How To Create Minimal Docker Images for Python Applications

How To Create Minimal Docker Images for Python Applications
Image by Editor | Midjourney & Canva

Creating minimal Docker images for Python apps enhances security by reducing the attack surface, facilitates faster image builds, and improves overall application maintainability. Let’s learn how to create minimal Docker images for Python applications.

Prerequisites

Before you get started:

  • You should have Docker installed. Get Docker for your operating system if you haven’t already.
  • A sample Python application you need to build the minimal image for. You can also follow along with the example app we create.

Create a Sample Python Application

Let's create a simple Flask application for inventory management. This application will allow you to add, view, update, and delete inventory items. We'll then dockerize the application using the standard Python 3.11 image.

In your project directory, you should have app.py, requirements.txt, and Dockerfile:

inventory_app/  ├── app.py  ├── Dockerfile  ├── requirements.txt  

Here’s the code for the Flask app for inventory management:

# app.py  from flask import Flask, request, jsonify    app = Flask(__name__)    # In-memory database for simplicity  inventory = {}    @app.route('/inventory', methods=['POST'])  def add_item():  	item = request.get_json()  	item_id = item.get('id')  	if not item_id:      		return jsonify({"error": "Item ID is required"}), 400  	if item_id in inventory:      		return jsonify({"error": "Item already exists"}), 400  	inventory[item_id] = item  	return jsonify(item), 201    @app.route('/inventory/', methods=['GET'])  def get_item(item_id):  	item = inventory.get(item_id)  	if not item:      		return jsonify({"error": "Item not found"}), 404  	return jsonify(item)    @app.route('/inventory/', methods=['PUT'])  def update_item(item_id):  	if item_id not in inventory:      		return jsonify({"error": "Item not found"}), 404  	updated_item = request.get_json()  	inventory[item_id] = updated_item  	return jsonify(updated_item)    @app.route('/inventory/', methods=['DELETE'])  def delete_item(item_id):  	if item_id not in inventory:      		return jsonify({"error": "Item not found"}), 404  	del inventory[item_id]  	return '', 204    if __name__ == '__main__':  	app.run(host='0.0.0.0', port=5000)  

This is a minimal Flask application that implements basic CRUD (Create, Read, Update, Delete) operations for an in-memory inventory database. It uses Flask to create a web server that listens for HTTP requests on port 5000. When a request is received:

  • For a POST request to /inventory, it adds a new item to the inventory.
  • For a GET request to /inventory/<item_id>, it retrieves the item with the specified ID from the inventory.
  • For a PUT request to /inventory/<item_id>, it updates the item with the specified ID in the inventory.
  • For a DELETE request to /inventory/<item_id>, it deletes the item with the specified ID from the inventory.

Now create the requirements.txt file:

Flask==3.0.3  

Next create the Dockerfile:

# Use the official Python 3.11 image  FROM python:3.11    # Set the working directory  WORKDIR /app    # Install dependencies  COPY requirements.txt requirements.txt  RUN pip install --no-cache-dir -r requirements.txt    # Copy the current directory contents into the container at /app  COPY . .    # Expose the port the app runs on  EXPOSE 5000    # Run the application  CMD ["python3", "app.py"]  

Finally build the image (we use the tag full to identify that this uses the default Python image):

$ docker build -t inventory-app:full .  

Once the build is complete you can run the docker images command:

$ docker images  REPOSITORY      TAG                 IMAGE ID       CREATED             SIZE  inventory-app   full                4e623743f556   2 hours ago         1.02GB  

You’ll see that this super simple app is about 1.02 GB in size. Well, this is because the base image we used the default Python 3.11 image has a large number of Debian packages and is about 1.01 GB in size. So we need to find a smaller base image.

Well, here are the options:

  • python:version-alpine images are based on Alpine Linux and will give you the smallest final image. But you need to be able to install packages as well, yes? But that’s a challenge with alpine images.
  • python:version-slim comes with the minimal number of Debian packages needed to run Python. And you’ll (almost always) be able to install most required Python packages with pip.

So your base image should be small. But not too small that you face compatibility issues and wrap your head around installing dependencies (quite common for Python applications). That’s why we’ll use the python:3.11-slim base image in the next step and build our image.

base-image
Choosing the Optimal Base Image | Image by Author

Use the Slim Python Base Image

Now rewrite the Dockerfile to use the python:3.11-slim base image like so:

# Use the official lightweight Python 3.11-slim image  FROM python:3.11-slim    # Set the working directory  WORKDIR /app    # Install dependencies  COPY requirements.txt requirements.txt  RUN pip install --no-cache-dir -r requirements.txt    # Copy the current directory contents into the container at /app  COPY . .    # Expose the port the app runs on  EXPOSE 5000    # Run the application  CMD ["python3", "app.py"]  

Let’s build the image (tagged slim):

$ docker build -t inventory-app:slim .  

The python:3.11-slim base image is of size 131 MB. And the inventory-app:slim image is around 146 MB which is much smaller than the 1.02GB image we had earlier:

$ docker images  REPOSITORY      TAG                 IMAGE ID       CREATED             SIZE  inventory-app   slim                32784c60a992   About an hour ago   146MB  inventory-app   full                4e623743f556   2 hours ago         1.02GB  

You can also use multi-stage builds to make the final image smaller. But that's for another tutorial!

Additional Resources

Here are a few useful resources:

  • Containerize Python Apps with Docker in 5 Easy Steps
  • python – Official Image | Docker Hub
  • Differences Between Standard Docker Images and Alpine Slim Versions

Bala Priya C is a developer and technical writer from India. She likes working at the intersection of math, programming, data science, and content creation. Her areas of interest and expertise include DevOps, data science, and natural language processing. She enjoys reading, writing, coding, and coffee! Currently, she's working on learning and sharing her knowledge with the developer community by authoring tutorials, how-to guides, opinion pieces, and more. Bala also creates engaging resource overviews and coding tutorials.

More On This Topic

  • Best Python Tools for Building Generative AI Applications Cheat Sheet
  • Create Synthetic Time-series with Anomaly Signatures in Python
  • Create a Dashboard Using Python and Dash
  • How To Create Custom Context Managers in Python
  • Containerize Python Apps with Docker in 5 Easy Steps
  • Knowledge Graph Forum: Technology Ecosystem and Business Applications
Follow us on Twitter, Facebook
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 comments
Oldest
New Most Voted
Inline Feedbacks
View all comments

Latest stories

You might also like...