How To Create Minimal Docker Images for Python Applications

This tutorial will teach you 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:

 

 

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.