# Containerizing Flask Applications with Docker

[Docker](https://www.docker.com/) lets you package your app and everything it needs, like libraries and tools, into one container so it runs the same anywhere.

When you use it with [Flask](https://flask.palletsprojects.com/), a lightweight Python web framework, you get a solid middle ground between running your own servers and using fully managed platforms like Heroku.

Flask works well with Docker because containers are much lighter than virtual machines. They don’t need to simulate a whole computer; they just share the host system’s core while staying isolated. This means fewer environment issues and easier, more reliable deployments.

In this guide, you'll containerize a Flask app with Docker.

[ad-uptime]

## Prerequisites

Before you begin, ensure you have the following installed:

- [Python](https://www.python.org/downloads/) 3.8 or newer
- [Docker](https://docs.docker.com/get-docker/) Desktop or Engine
- Basic familiarity with Flask applications 

## Setting up the project structure

In this section, you'll build a simple Flask app structure that works well with Docker. The setup is straightforward, with just one application file.

Start by creating a new directory for your project and navigating into it:

```command
mkdir flask-docker-app && cd flask-docker-app
```

Next, set up a virtual environment to manage your Python dependencies separately:


```command
python3 -m venv venv
```

Activate the virtual environment:

```command
source venv/bin/activate
```

Install Flask and Gunicorn:

```command
pip install flask gunicorn
```

Create a `requirements.txt` file to track dependencies:

```command
pip freeze > requirements.txt
```

Now, create a simple `app.py` file as the single entry point for your application:

```python
[label app.py]
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def index():
    return jsonify({"message": "Welcome to Flask in Docker!"})

@app.route('/health')
def health():
    return jsonify({"status": "healthy"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)
```

This straightforward structure provides:

- A simple Flask application in a single file
- A welcome endpoint that returns JSON
- A health check endpoint for container monitoring
- Configured to listen on all interfaces (required for Docker)


Let's start by testing our Flask application without Docker. Launch the Flask development server with:

```command
flask run
```

```text
[output]
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
```

Visit `http://127.0.0.1:5000/` in your browser, and you should see a JSON response:

```json
{"message": "Welcome to Flask in Docker!"}
```
![Screenshot of flask JSON response in the browser](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/3ca4daa3-0ffb-4fb5-5f4c-f6aab0aee500/md1x =3248x1996)

After verifying it works, stop the server.



## Getting started with Docker

In this section, you'll enhance your Flask application by adding Docker. With Docker, your application will gain portability, consistency, and isolation, making it easier to deploy and scale.

First, create a simple `Dockerfile` in the root of your project:

```command
touch Dockerfile
```

Open the `Dockerfile` and add the following straightforward configuration:

```dockerfile
[label Dockerfile]
FROM python:3.13.3-slim-bookworm

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install gunicorn

# Copy application code
COPY app.py .

EXPOSE 5000

# Run with Gunicorn for production
CMD ["gunicorn", "--bind", "0.0.0.0:4000", "app:app"]
```

Open the `Dockerfile` and add the following configuration. This `Dockerfile` will define how Docker builds your application image. 

It starts with a slim Python 3.13.3 base image to keep the image lightweight. The working directory inside the container is set to `/app`, and it installs dependencies from the `requirements.txt` file. 

It also installs Gunicorn for serving the app in production. The application code, `app.py`, is copied into the container, and port 5000 is exposed to handle incoming connections. Finally, the application is run with Gunicorn as the production WSGI server, binding it to `0.0.0.0:4000`.

Next, create a simple `.dockerignore` file to prevent unnecessary files from being included:

```command
touch .dockerignore
```

Add the following contents:

```text
[label .dockerignore]
venv/
__pycache__/
*.pyc
.git/
.gitignore
.idea/
.vscode/
```

Now, build the Docker image:

```command
docker build -t flask-app .
```

After the build completes, run the container:

```command
docker run --name flask-container -d -p 4000:4000 flask-app
```

The `-d` flag runs the container in detached mode (background), and the `-p` flag maps port 5000 in the container to port 5000 on your host.

Your Flask application is now running inside a Docker container! Visit `http://localhost:4000/` to verify it's working:

![Screenshot confirming that the flask application is now running inside a Docker container](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/541dde86-04a7-462c-4001-71b00ca3d300/md1x =3248x1996)

This confirms the app is up and running.

## Working with container logs

Managing logs is essential when working with containerized applications. Docker automatically captures everything written to standard output (stdout) and standard error (stderr) by your application, making it easy to inspect what's happening inside the container.

You can view the logs of your running container with:

```command
docker logs flask-container
```

```text
[output]
[2025-05-08 16:44:18 +0000] [1] [INFO] Starting gunicorn 23.0.0
[2025-05-08 16:44:18 +0000] [1] [INFO] Listening at: http://0.0.0.0:4000 (1)
[2025-05-08 16:44:18 +0000] [1] [INFO] Using worker: sync
[2025-05-08 16:44:18 +0000] [7] [INFO] Booting worker with pid: 7
```

These logs show the Gunicorn WSGI server starting up successfully. The first line indicates Gunicorn's version, followed by the listening address and port. The third line shows it's using the default synchronous worker type, and the last line confirms a worker process has started with process ID 7.

You can follow the logs in real-time (similar to `tail -f`) with:

```command
docker logs -f flask-container
```

This is particularly useful during development or when troubleshooting issues, as you'll see log entries appear as they happen.

To see only the most recent entries:

```command
docker logs --tail 50 flask-container
```

And for logs within a specific time range:

```command
docker logs --since 2025-05-08T16:00:00 flask-container
```

When you're done with the container, you need to stop and remove it:

```command
docker stop flask-container
```
```command
docker rm flask-container
```

For simplicity and automatic cleanup, use the `--rm` flag when starting, which will automatically remove the container when it stops:

```command
docker run --name flask-container --rm -d -p 5000:5000 flask-app
```

This prevents accumulating stopped containers that take up disk space on your system.


## Setting up Docker Compose for development

Now that you've successfully containerized your Flask application with Docker, let's improve your development workflow using Docker Compose. This tool simplifies managing Docker applications, especially when you need multiple containers working together.

Before proceeding, make sure to stop your currently running container from the previous section:

```command
docker stop flask-container
```

First, create a `docker-compose.yml` file in your project root:

```command
touch docker-compose.yml
```

Add the following configuration:

```yaml
[label docker-compose.yml]
services:
  web:
    build: .
    ports:
      - "4000:5000"
    volumes:
      - ./app.py:/app/app.py
    environment:
      - FLASK_DEBUG=True
    command: python app.py
    restart: unless-stopped
```
This configuration builds your application using the Dockerfile, maps port `4000` on the host to port `5000` in the container, and mounts the local `app.py` to the container for live code reloading. 

It sets the `FLASK_DEBUG` environment variable for development, uses Flask's development server by overriding the default command, and ensures the container restarts automatically if it crashes.
Start your development environment:

```command
docker-compose up
```

You'll see output showing your application starting up:

```text
[output]
 ✔ Network flask-docker-app_default  Created                     0.0s 
 ✔ Container flask-docker-app-web-1  Created                     0.0s 
Attaching to web-1
web-1  |  * Serving Flask app 'app'
web-1  |  * Debug mode: on
web-1  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
web-1  |  * Running on all addresses (0.0.0.0)
web-1  |  * Running on http://127.0.0.1:5000
web-1  |  * Running on http://172.18.0.2:5000
web-1  | Press CTRL+C to quit
web-1  |  * Restarting with stat
web-1  |  * Debugger is active!
web-1  |  * Debugger PIN: 666-640-080
```

Now, you can edit your `app.py` file locally, and the changes will be reflected immediately in the running container thanks to the volume mapping. Try adding a new route to `app.py`:

```python
[label app.py]
...
[highlight]
@app.route('/hello')
def hello():
    return jsonify({"message": "Hello from Docker Compose!"})
[/highlight]

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)
```

Save the file and visit `http://localhost:4000/hello` in your browser:

![Screenshot showing that Docker Compose is serving the flask app](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/919bd89d-d824-4b41-3e56-52063f739500/public =3248x2002)

You'll see the changes without rebuilding the image or restarting the container.

To run the containers in the background, use the `-d` flag:

```command
docker-compose up -d
```

```text
[output]

[+] Running 1/1
 ✔ Container flask-docker-app-web-1  Started                     0.2s 
```

View logs from Docker Compose:

```command
docker-compose logs
```

And to shut it down when you're done:

```command
docker-compose down
```

This Docker Compose setup gives you a more flexible development workflow than plain Docker commands and prepares you to add additional services like databases.


## Final thoughts

Containerizing your Flask app with Docker ensures consistent deployment and scalability across environments. Docker Compose simplifies managing multi-container applications, making setting up, scaling, and maintaining development and production environments easier.


For more detailed documentation on Docker and Docker Compose, refer to the official [Docker documentation](https://docs.docker.com/) and [Docker Compose documentation](https://docs.docker.com/compose/). These resources provide deeper insights into container management, multi-container setups, and best practices for production-ready applications

