In this tutorial, you will learn how to Dockerise a Python app with PostgreSQL database. We will dockerise the Python Flask app that we created in previous tutorial Build API with Python Flask. However the same setup can be used to dockerise Django apps or any other Python apps.
Prerequisites
This tutorial requires basic level Docker understanding. You can follow the two other articles in my Docker tutorial series.
Following sections assume that you have installed Docker on your machine and you have basic understanding of Docker.
Create a Virtual Network
In sections ahead, we will run two separate docker containers; one hosting the PostgreSQL and the other hosting our flask-api-example
app. Now think of these two separate containers as two separate machines which means that there needs to be a network over which our app can communicate with the DB.
Docker lets us create a virtual network with a simple command. We’ll add both our containers on that network and will use it to create DB connection from our dockerised app.
To create a Docker network, execute following command in your Terminal.
> docker network create dock-net
This command creates a simple bridge
network with name dock-net
which is enough for our needs 🙂
You can use following command to see which networks are available.
> docker network ls
In the output, you should see the dock-net
network along with the default ones.
Run a PostgreSQL Docker Container
This step is very simple and straightforward. Like we did in Getting Started tutorial, we’ll use a Docker image from Dockerhub but this time it will be a PostgreSQL image. You can choose a postgres
image tag of your choice based on which version of PostgreSQL you need. This tutorial, however, uses PostgreSQL 12.2 and accordingly the Docker image tag is postgres:12.2
.
Open Terminal app and execute following command.
> docker container run -p 5433:5432 --network dock-net --name pg-container -e POSTGRES_PASSWORD=1234 postgres:12.2
This command will create a new container running a PostgreSQL instance. Let’s take a look at the components of this command.
-p 5433:5432
:
-p
(or --publish
) is used to publish a container’s port(s) to the host. Here we are publishing container’s port 5432 (Postgres default port) and mapping it to host’s port 5433.
--network dock-net
:
With --network
option we are adding this container on the network dock-net
that we created above.
--name pg-container
:
For easy reference, we can use optional --name
option so we are specifying the name for this container as pg-container
. If we don’t specify a name, Docker will give it a random name.
Although this name has other uses also but in our current example, its most important use is that this name pg-container
is also the identifier of this container on our dock-net
virtual network. Therefore, we will use pg-container
as DB host name in DB connection in our app configs.
-e POSTGRES_PASSWORD=1234
:
-e
is used to pass environment variables in container. Here we are passing POSTGRES_PASSWORD
which is mandatory to be provided for the DB user postgres
.
postgres:12.2
This is the Docker image that we are using to create PostgreSQL container. postgres
is the name of image while 12.2
is the tag depicting the PostgreSQL version as well.
When you execute the docker container run
command, Docker looks into the local repository, if it doesn’t find this image, it downloads it from Dockerhub.
Testing the DB connection from Host
To test the PostgreSQL DB connection from host machine, you can use any DB client app like PGAdmin or you can also use command line utility psql
as well.
Below is the image showing the connection details in PGAdmin 4. Please note that we are using port 5433
of localhost
. This is because we mapped port 5432
(default PostgreSQL port) to host’s port 5433
. As we are now connecting from host (which is out of dock-net
virtual network so we must use the mapped port of the localhost.

Create Application Database
Now that we have connected to PostgreSQL host, we can create the database for our Python Flask app. So just go head and create a new DB with name of your choice 🙂
At this step we have an empty DB having no tables!
Dockerise the Python app
So we are using the same app that we created in our previous tutorial, Build API with Python Flask. In that tutorial we did the app setup in traditional way. Now we will dockerise it by adding a Dockerfile.
Dockerfile
- Add a folder named
docker
under application root folder. Naming this folder asdocker
is not mandatory, you can name it as you wish. - Under this
docker
folder add fileDockerfile
with following content:
# Start with Python 3.8 base image
FROM python:3.8-alpine
# Show stdout and stderr outputs instantly without buffering
ENV PYTHONUNBUFFERED 1
# Create application root directory in container
RUN mkdir /dockerised-example
# Set dockerised-example as working directory
WORKDIR /dockerised-example
# Copy requirements.txt in application root directory
ADD requirements.txt /dockerised-example/
# Install dependencies
RUN \
# Install curl
apk add --no-cache curl && \
# Install postgres utilities which are required by `psycopg2` lib being used by app
apk add --no-cache postgresql-libs && \
apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev && \
# Install Python packages required by the app
python3 -m pip install -r requirements.txt --no-cache-dir && \
apk --purge del .build-deps
# Add all files from current directory on host to dockerised-example directory in container
ADD ./ /dockerised-example/
COPY docker/entrypoint.sh /sbin/entrypoint.sh
# Expose a port so that we can map it with a host port
EXPOSE 5000
# Execute command when container starts (not executed with image build)
ENTRYPOINT ["/sbin/entrypoint.sh"]
I have explained all steps in the Dockerfile with inline comments. Please go through them. You can once again note that Dockerfile essentially contains a set of commands that we’d otherwise perform manually at the time of deployment.
Please note that every step written in the Dockerfile becomes an image layer. When you next time build your image, Docker checks if a layer exists already then it uses it from cache so saving the resources and time.
If your Docker file has 9 steps and in the next build of image, if the change has made at step 6, all layer 6-onwards will be built again. This means that arranging your steps in a way that most frequently changed parts are at the bottom will save you quite much build time.
entrypoint.sh
- Add another file
entrypoint.sh
under folderdocker
. The content of this file should be:
#!/bin/sh
set -o errexit -o nounset -o pipefail
cd /dockerised-example
flask run --host=0.0.0.0
- Please note that you can write any commands that you’d like to execute at the time of your application startup. It can be a good place for database migrations as well.
For our purpose, we have to execute Flask’s development server which is enough within the scope of this tutorial so we just add flask run
command and before that we obviously cd
to application folder.
Set Application Config
We are using .env
file to set the the environment based application configs. So let’s make that one important change in .env
file we created in previous tutorial Build api with Python Flask.
- As it was mentioned above that the identifier of our DB host container is
pg-container
on the virtual networkdock-net
. Our application container will also join this network so it needs to useDB_HOST=pg-container
. - Also set
DB_NAME
,DB_USERNAME
andDB_PASSWORD
. - Finally,
.env
file should appear like below:
FLASK_ENV=development
FLASK_DEBUG=true
APP_BASE_URL=127.0.0.1
APP_SECRET_KEY=dev
DB_HOST=pg-container
DB_PORT=5432
DB_NAME=<db_name>
DB_USERNAME=<username>
DB_PASSWORD=<password>
SESSION_LENGTH=2592000
Build the Application’s Docker image
As you know, to run a container, we need an image. So we have to first build
the Docker image.
- Make sure you are in application’s root folder.
- Run the following command:
> docker build -t flask-docker-exp:latest . -f docker/Dockerfile
Let us see what are the components of this command.
-t flask-docker-exp:latest
:
-t
(or --tag
) option is used the specify the name and tag of the image. Here we have named the image flask-docker-exp
and latest
is the tag.
.
This .
specifies the context as current folder. As we are executing the docker build
command from application’s root folder, so it will be the context for docker build
.
-f docker/Dockerfile
-f
(or --file
) option is used to specify the path of Dockerfile. This is an optional param and if it is not specified Docker will look for a Dockerfile current folder.
If you want to file name other than default name Dockerfile
, you can do so. In that case too, you can use -f
option to specify the custom name of your Dockerfile.
Run the application Docker container
Now that we have build the Docker image for our application. Its time to run our Dockerised application!!
- Remember that our database host is running in its own container.
- We created, in steps above, a virtual network named
dock-net
. - We already added our DB container
pg-container
on that networkdock-net
.pg-container
is therefore the identifier on this network of our DB host. - Also we said (as it makes sense!) that we’ll add our application container on
dock-net
network so that both containers can communicate.
So keeping all that in mind, let us run our first dockerised application!
- Run the following command in your Terminal.
> docker container run -p 8000:5000 --network dock-net --name flask-api-exp flask-docker-exp:latest
Again, let us go through each component of docker container run
command and see what they do.
-p 8000:5000
:
Same as explained above. We are publishing container’s port 5000
(default port that Flask development server uses) and mapping it to the host’s port 8000
. This means that localhost:8000
(or 127.0.0.1:8000
) will send the requests to port 5000
of this container.
--network dock-net
:
With --network
option, We are adding our application’s container on the virtual network dock-net
.
--name flask-api-exp
:
For easy reference to our app container, we are using optional --name
parameter and naming the container flask-api-exp
. If we don’t tell a name, Docker will assign it a random name.
flask-docker-exp:latest
:
flask-docker-exp
is the name and latest
is the tag of our Docker image which we are using to run a container.
Test the API endpoints
Now that our first Dockerised application is running so we can go ahead and test the APIs like before. We can use curl
or using any API client tool like Postman to test the APIs we created in our Python Flask app and have dockerised it today!
Below are the Postman images, showing the requests and responses. Please note that we are using port 8000
of the host localhost
(127.0.0.1
) as we mapped it with port 5000
of our application container.


What’s more to do?
So far we have done very basic Docker setup. There is a lot more to do.
DB data is not persistent
If you remove your container, you’ll lose the DB data. However this shouldn’t happen. We will fix this issue in our next tutorial.
App container’s image needs to be build every time when code changes
This is important especially if we want to setup our development environment using Docker. Current setup is not enough for development environment as we need to build image every time we change the code.
In next tutorial we’ll focus on these issues to improve our Docker setup 🙂