Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: April 20, 2025
When working with Docker, running a Gunicorn server can significantly improve the scalability and performance of applications, for instance, Flask applications. However, transitioning from running a Flask app directly with flask run to running it with Gunicorn can be challenging.
In this tutorial, we present a step-by-step guide for running a Gunicorn server in Docker.
Gunicorn, a Python WSGI HTTP server, enables us to run Flask, Django, or other Python web applications efficiently. To explain, Flask’s built-in development server is not suitable for production since it’s single-threaded, unlike Gunicorn, which can handle multiple requests simultaneously.
Meanwhile, Docker facilitates us with an isolated environment for packaging our application with all its dependencies, ensuring consistency across different environments.
Now, running a Gunicorn Server in Docker alongside our Flask application enables the Flask application to handle multiple requests. So, Gunicorn manages multiple worker processes to handle requests concurrently, offloads WSGI handling from Flask’s built-in server, and we can fine-tune it with worker types, logging, and timeouts.
In this section, we create a simple project to show how we can set up and run a Gunicorn server in Docker.
First, let’s create a simple Flask application:
from flask import Flask, request, jsonify
app = Flask(__name__)
default_message = "Hello, this is a Flask app running on Gunicorn inside Docker!"
@app.route('/', methods=['GET'])
def home():
return jsonify({"message": default_message})
if __name__ == "__main__":
app.run(debug=False, host='0.0.0.0', port=8080)
For this example, let’s name the file flaskfile.py. Although we use app.run() for local development, we set Gunicorn as the server when running inside Docker.
Next, let’s create the file in the same directory as the Flask application and add the content:
# Use an official Python runtime as a parent image
FROM python:3.9
# Handles specifying the working directory within the container
WORKDIR /code
# Copy the local files to the container
COPY . /code
# Install required dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Configure the Flask application to listen on port 8080
EXPOSE 8080
# Start Gunicorn with 4 worker processes
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8080", "flaskfile:app"]
Here is the breakdown:
To clarify, “flaskfile:app” refers to the app object specified inside the flaskfile.py file.
In addition, we need to ensure we have the requirements.txt file:
flask
gunicorn
Above, we list all the necessary dependencies.
Now, let’s build the image:
$ docker build -t my_flask_app .
The command above builds the image my_flask_app based on the instructions specified in the Dockerfile.
After this, let’s run the container:
$ docker run -it -p 8080:8080 my_flask_app
In this command:
At this point, let’s verify that the application runs as expected.
Once the container is up and running, let’s test our Flask API:
$ curl http://localhost:8080/
{"message":"Hello, this is a Flask app running on Gunicorn inside Docker!"}
For the testing, we use curl.
Here are some best practices we can consider for production.
Firstly, we can add the –access-logfile flag to enable Gunicorn logging:
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8080", "--access-logfile", "-", "flaskfile:app"]
The above modification enables us to monitor traffic and troubleshoot issues more easily.
Secondly, we can apply load balancing. We can consider deploying multiple containers behind the reverse proxy, such as Nginx, for larger applications to distribute traffic efficiently across multiple Gunicorn instances.
Further, we can use environment variables to avoid hard-coding configurations, such as ports, for instance, defining the configurations in a .env file.
Here are a few issues we may encounter while running Gunicorn in Docker and how to resolve them.
So, we may encounter a situation where the port is already in use. In this case, the port 8080 is already occupied on our machine. In this situation, we can map a different host port:
$ docker run -it -p 5000:8080 my_flask_app
As a result of this change, we can access the application at http://localhost:5000/.
At times, the Gunicorn workers may crash when a request takes too long:
CMD ["gunicorn", "-w", "4", "-t", "60", "-b", "0.0.0.0:8080", "--access-logfile", "-", "flaskfile:app"]
Above, we handle requests that take too long by setting the timeout to 60 seconds.
Another issue that’s not uncommon is the application import error. To explain, we need to ensure that we write flaskfile.py file correctly, whereby the app object is defined at the top level:
from flask import Flask
app = Flask(__name__)
This enables Gunicorn to import the app object.
In this article, we explored how to run a Gunicorn server in Docker.
So, running a Gunicorn server in Docker provides an efficient way to deploy Python web applications in a production-like environment. To demonstrate, we developed a Flask app running inside Docker with Gunicorn as the WSGI server.
Thus, we can now containerize and scale Python web applications using Gunicorn and Docker.