Write a Dockerfile for a custom Docker image

Create a custom Docker image by writing a Dockerfile with essential instructions for containerizing applications.

M Bytes Newsletter
Get the developer newsletter

    Fresh bytes every Thursday. No spam, ever. Unsubscribe anytime.

    Join 9,000+ developers and get three free video lessons every week.

    A Dockerfile is just a text file that contains instructions for building a Docker image. Think of it as a recipe that tells Docker exactly how to create your application environment.

    Let's look at a practical example of serving a simple Python web application, and break down what’s going on with this list of instructions:

    # Start with Python's official image
    FROM python:3.13.1
     
    # Run a shell command inside the image
    RUN pip install flask
     
    # Set the port our Flask app will run on
    ENV PORT=8000
     
    # Set up our working directory
    WORKDIR /app
     
    # Copy our application code into the image
    COPY app.py .
     
    # Tell Docker what command to run when starting
    CMD ["python", "app.py"]
    • Lines starting with # are comments. Docker will ignore these lines when it builds your image.
    • The first line FROM python:3.13.1 specifies our base image. You can find official Docker images at https://hub.docker.com, which is the official public repository of Docker images. We can then find Python's official Docker image and see all of the versions available for this image. The 3.13.1 after the colon specifies which version of the image that we want to use as the base for our custom image. You’ll also notice that there are many other variations of this tag available, such as 3, latest, and 3.13.1-bookworm. I’d recommend using the most specific version possible, as using versions such as 3 or latest can introduce updates which can break your app. This is why I picked 3.13.1 for this Docker image.
    • RUN executes any shell command. Here, we're installing the Flask package using pip, but this could be any command you'd normally type in your terminal.
    • ENV sets environment variables that will be available to our application. Flask apps typically run on port 5000 in development, but we're using 8000 here, as it's a common port to run Python apps on within production.
    • WORKDIR sets up where our application will live in the image. /app is a common convention to use for your app code in containers.
    • COPY takes two arguments: a source (where the file is on your computer), and a destination (where you want it in the container). The . means "current directory", so COPY app.py . copies app.py into our working directory (/app).
    • CMD defines what command runs when the container starts. We use the square brackets format (called "exec form") ["python", "app.py"] instead of python app.py because this is what runs our Python process directly as PID (process ID) 1, without wrapping it in a seperate shell. This ensures our app can handle shutdown signals properly, and makes the container run more efficiently.

    For this to run, we’ll also need an app.py file present next to our Dockerfile. Here is an example of a Flask application which we can use when it comes time to building our image:

    from flask import Flask
    import os
     
    app = Flask(__name__)
     
    @app.route('/')
    def hello():
        return 'Hello from Docker!'
     
    if __name__ == '__main__':
        port = int(os.environ.get('PORT', 5000))
        app.run(host='0.0.0.0', port=port)

    Quick note: Running this script with host='0.0.0.0' tells Flask to listen on all network interfaces, not just localhost. This is an important concept to use for Docker-based apps, because otherwise our app wouldn't be accessible from outside the container.

    The Dockerfile contains everything that is needed to properly containerize a Python app. Each instruction builds on the previous one, which results in creating a complete environment which can run our app.