In this article, I will provide some examples of how to create database containers using Docker.
The focus of this article is to assist beginners in using Docker in their projects, addressing questions about creating docker-compose
or Dockerfile
.
As a reference for the most popular databases, I used the top 9 from the Stack Overflow survey of 2023.
Summary
PostgreSQL
In our Dockerfile, it will be quite simple. We will use the PostgreSQL image in version 16 and expose port 5432 so that it is accessible by our container.
FROM postgres:16
EXPOSE 5432
Let’s add the database container to our services and assign a name to it, in this case, postgres_db
.
services:
database:
container_name: postgres_db
In the build tag, we will indicate where our Dockerfile
is located, which will be used to configure our Postgres container.
build:
context: .
dockerfile: Dockerfile.postgres
In the context, we use the dot to indicate that the Dockerfile is located in the same directory as the docker-compose.
As PostgreSQL uses the default user postgres
, we will only add a password for our user in the environment section.
environment:
POSTGRES_PASSWORD: 12345678
Now we need to add the communication ports between the host machine and the container, this will create a connection between the two, making it possible to access the database that was created in the container.
ports:
- "5432:5432"
Creating volumes is optional, but it's an interesting thing to do when we're using databases. It allows for data persistence in our container, and even if it's destroyed, we can recreate the container with the data intact from the volume. This is a useful feature for maintaining data consistency and durability.
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Finally, we will create a Network. They are used to facilitate communication between our services. For example, let’s say you want to create a Compose that includes Postgres and pgAdmin. To facilitate interaction between them, we add both to the same network to assist in communication (Example here).
networks:
- mynet
networks:
mynet:
driver: bridge
Docker-compose
version: '3.8'
services:
database:
container_name: postgres_db
build:
context: .
dockerfile: Dockerfile.postgres
environment:
POSTGRES_PASSWORD: 12345678
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- mynet
volumes:
postgres_data:
networks:
mynet:
driver: bridge
MySQL
For the MySQL Dockerfile, we will make the same configuration as PostgreSQL, with the difference that we will expose the default port of MySQL.
FROM mysql:8.3
EXPOSE 3306
The docker-compose will follow the same structure as the Postgres compose, with the difference that in the environment section more fields will be added besides the user’s password.
environment:
MYSQL_ROOT_PASSWORD: 12345678
MYSQL_DATABASE: mydb
MYSQL_USER: admin
MYSQL_PASSWORD: admin123
The MYSQL_ROOT_PASSWORD
field, as the name suggests, will be the password for the root user. Do not confuse it with the MYSQL_PASSWORD
field, which is the password for the user defined in the MYSQL_USER
field. The MYSQL_DATABASE
field is the name of the database that will be created when building the container. You can create other databases after the container is created using MySQL commands or a database client, such as DBeaver (Example here).
Docker-compose
version: '3.8'
services:
database:
container_name: mysql_db
build:
context: .
dockerfile: Dockerfile.mysql
environment:
MYSQL_ROOT_PASSWORD: 12345678
MYSQL_DATABASE: mydb
MYSQL_USER: admin
MYSQL_PASSWORD: admin123
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- mynet2
volumes:
mysql_data:
networks:
mynet2:
driver: bridge
SQLite
SQLite is different from the other databases mentioned here. It is a disk-based
database, which means it stores the data generated in a file on the system itself and does not depend on a server, like the other databases presented. Generally, the files are saved with the .db
extension.
It is widely used in mobile applications, mainly on Android, due to its presence on many devices.
Dockerfile
For the SQLite Dockerfile, we will use an image of a Linux distribution called Alpine, known for its lightness, which makes it a great option for Docker containers.
FROM alpine:3.19
RUN apk add --no-cache sqlite
WORKDIR /database
In this Dockerfile, we install SQLite in our container and create a directory called "database", where we will execute our SQLite commands and save the databases created.
Docker-compose
The SQLite Compose will follow a structure similar to that of Postgres and MySQL, but with some differences.
The first difference is in the volumes, where we will specify the directory in which the volume will be mounted, allowing for more efficient manipulation of the container data.
volumes:
- ./sqlite_data:/database
The second difference is the tail -f /dev/null
command. Since SQLite is a disk-based
database and does not have a server to keep the container active, we use this command to keep the container running.
command: ["tail", "-f", "/dev/null"]
The tail
command is used to display the last lines of a file, while the -f
argument is used to specify the file that the command should follow waiting for new lines. By specifying /dev/null
, which contains nothing, the command will not display any content in the terminal, thus keeping our container active so that we can use the SQLite commands in the terminal.
version: '3.8'
services:
database:
container_name: sqlite_db
build:
context: .
dockerfile: Dockerfile.sqlite
volumes:
- ./sqlite_data:/database
networks:
- mynet3
command: ["tail", "-f", "/dev/null"]
volumes:
sqlite_data:
networks:
mynet3:
driver: bridge
MongoDB
MongoDB is a NoSQL database, unlike the others presented so far, which were SQL, that is, relational databases. Mongo is a document-oriented database and is widely used for its flexibility, scalability, and ease of manipulation.
Dockerfile
Our Dockerfile will follow the same pattern as the others. We will use the latest version and we will expose the default port of MongoDB, which is 27017
.
FROM mongo:latest
EXPOSE 27017
Docker-compose
In the MongoDB docker-compose, we will provide two environment variables. The first is MONGO_INITDB_ROOT_USERNAME
, to define the root user, and the second is MONGO_INITDB_ROOT_PASSWORD
, where we will add the password for this user.
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: 12345678
We will also use mongo-express
, which is a web interface to facilitate the manipulation of Mongo, similar to PgAdmin for PostgreSQL.
mongo-express:
image: mongo-express
To function correctly, we need to inform in the mongo-express variables the user and password that we created for MongoDB, in addition to the database URL.
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: 12345678
ME_CONFIG_MONGODB_URL: mongodb://admin:12345678@database:27017/
The structure of the URL is as follows: <Driver>://<user>:<password>@<host>:<port>/
. As we are using the URL externally, we will use the name of our MongoDB service, which is database
. If you are going to make the connection inside the Mongo container using mongosh, you will have to replace the host with localhost
.
To be able to access mongo-express, it will be necessary to create a username and password. This will be done through the ME_CONFIG_BASICAUTH_USERNAME
and ME_CONFIG_BASICAUTH_PASSWORD
variables.
ME_CONFIG_BASICAUTH_USERNAME: mongo
ME_CONFIG_BASICAUTH_PASSWORD: 123456
It is necessary for the services to use the same network to facilitate communication between them.
version: '3.8'
services:
database:
container_name: mongo_db
build:
context: .
dockerfile: Dockerfile.mongo
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: 12345678
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
networks:
- mynet4
mongo-express:
image: mongo-express
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: 12345678
ME_CONFIG_MONGODB_URL: mongodb://admin:12345678@database:27017/
ME_CONFIG_BASICAUTH_USERNAME: mongo
ME_CONFIG_BASICAUTH_PASSWORD: 123456
depends_on:
- database
ports:
- "8081:8081"
networks:
- mynet4
volumes:
mongo_data:
networks:
mynet4:
driver: bridge
Microsoft SQL Server
The SQL Server, unlike other databases, has its image maintained by the Microsoft Container Registry
, instead of the Docker Hub.
Dockerfile
The image used is the Linux version of SQL Server. For more information or to use a specific database image, access here.
FROM mcr.microsoft.com/mssql/server:2022-latest
EXPOSE 1433
Docker-compose
Unlike other banks, it is necessary to use the ACCEPT_EULA
variable to confirm that you accept the terms of the End User License Agreement. And in the MSSQL_SA_PASSWORD
variable, it is necessary to follow Microsoft's password policy (see here).
version: '3.8'
services:
database:
container_name: sqlserver_db
build:
context: .
dockerfile: Dockerfile.sqlserver
environment:
ACCEPT_EULA: Y
MSSQL_SA_PASSWORD: Admin123#
ports:
- "1455:1433"
networks:
- mynet5
networks:
mynet5:
driver: bridge
For more information about the image, instructions, and how to use SQL Server commands, access the official Microsoft documentation here.
Redis
Redis, like MongoDB, is also a NoSQL database. It is an in-memory database that uses a key-value data structure and is widely used for cache
.
Docker-compose
The Redis Compose will be simpler. We will use the image
tag to indicate which image our container will pull, and we will also expose the default Redis port, which is 6379
.
image: redis
ports:
- "6379:6379"
In Redis, we can also customize its configuration through a file called redis.conf
, where we can define, for example, which addresses it can connect to, how much memory will be used, specify the protocol that can be used, and many other settings. See more about Redis configurations here.
version: '3.8'
services:
database:
image: redis
container_name: redis_db
ports:
- "6379:6379"
networks:
- mynet6
networks:
mynet6:
driver: bridge
MariaDB
We will use version 11.2.3
of MariaDB and, like MySQL, we will expose port 3306.
FROM mariadb:11.2.3
EXPOSE 3306
Docker-compose
The MariaDB docker-compose will be similar to MySQL's, with the only difference in the environment being that we will only swap the MYSQL
prefix for MARIADB
.
environment:
MARIADB_ROOT_PASSWORD: 13246578
MARIADB_DATABASE: mydb
MARIADB_USER: admin
MARIADB_PASSWORD: admin123
We will also have an example of docker-compose using DBeaver here.
version: '3.8'
services:
database:
build:
context: .
dockerfile: Dockerfile.mariadb
environment:
MARIADB_ROOT_PASSWORD: 13246578
MARIADB_DATABASE: mydb
MARIADB_USER: admin
MARIADB_PASSWORD: admin123
ports:
- "3306:3306"
volumes:
- mariadb_data:/var/lib/mysql
networks:
- mynet7
volumes:
mariadb_data:
networks:
mynet7:
driver: bridge
Elasticsearch
Elasticsearch is another NoSQL database on our list. It is widely used as a search engine, for log analysis, business analysis, and other purposes.
For a better experience, I will create a container using Elasticsearch and Kibana
, which is a data visualization plugin used with Elastic. Both are part of the Elastic Stack (or ELK Stack), which includes other tools such as Beats and Logstash.
Dockerfile
In our Dockerfile, we will use version 8.12.1
of Elasticsearch. We will also create a directory where we will copy a script to execute two necessary commands to use Elastic with Kibana. Finally, we will expose the default Elasticsearch port, which is 9200.
FROM docker.elastic.co/elasticsearch/elasticsearch:8.12.1
WORKDIR /elastic
COPY setup_elasticsearch.sh /elastic
EXPOSE 9200
In this script, we will execute two necessary commands. The first is to get the password for the elastic
user, and the second command will be to generate the Kibana token. Basically, both commands are being executed and responding that we want to reset the password and generate the token.
#!/bin/bash
echo "y" | /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
echo "y" | /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana
The use of the script is optional; you can execute the commands manually through the container terminal. But if you want to use it, just enter the container terminal and execute it by writing
./setup_elasticsearch.sh
.
Docker-compose
The Compose will have some different environment variables so far. We will use only two for this example, but it is worth remembering that there are several others in the Elasticsearch and Kibana documentation.
We are using the discovery.type
variable as 'single-node'. By default, Elasticsearch allows the cluster to be 'multi-node', which allows connecting and joining several nodes in the same cluster.
And the second environment variable we will use is ES_JAVA_OPTS
, which defines the initial size of the heap
memory that Elasticsearch can use. In this case, we are defining the size as 1GB.
version: '3.8'
services:
elasticsearch:
build:
context: .
dockerfile: Dockerfile.elastic
container_name: Elasticsearch
environment:
discovery.type: "single-node"
ES_JAVA_OPTS: "-Xms1g -Xmx1g"
ports:
- "9200:9200"
networks:
- mynet8
kibana:
image: docker.elastic.co/kibana/kibana:8.12.1
container_name: kibana
ports:
- "5601:5601"
networks:
- mynet8
networks:
mynet8:
driver: bridge
Oracle
We are back to SQL databases, and for this example, I will use the 23c Free
version of the Oracle database. Finding the Oracle image can be a bit more complicated, as like Microsoft with SQL Server, Oracle also maintains its own images, which makes it a bit harder to find and use in Docker. The version I will use can be found here.
Docker-compose
Our Compose will be simple, it will only be necessary to inform the password of our user in the environment variables.
environment:
ORACLE_PWD: 12345678
You can also modify the database character set using the ORACLE_CHARACTERSET
variable, but it is optional. The default value is set to AL32UTF8
.
version: '3.8'
services:
database:
image: container-registry.oracle.com/database/free:latest
container_name: oracle
environment:
ORACLE_PWD: 12345678
ports:
- "1521:1521"
volumes:
- "oracle_data:/opt/oracle/oradata"
networks:
- mynet9
volumes:
oracle_data:
networks:
mynet9:
driver: bridge
To learn more about this database, here are some useful links. Get Started teaches you how to install Oracle in other ways and also shows you how to connect to SQL via the terminal and in various languages. Oracle Database 23c to learn more about this version. And also the Oracle Container Registry if you want to obtain images of other Oracle products or even use other versions of the database.
Conclusion
I hope this article helps someone who is just starting out and takes some of the hassle out of creating a Docker container, as creating a database container is something we use a lot in our day-to-day lives. I'll leave here the link to the repository with all the examples.
Top comments (2)
Being security sensitive, I'm looking askance at putting admin passwords into the configurations the way this seems to be doing.
Is that really best practice?
It's not the best practice when thinking about something for production, what was shown in the article is more for development environments or studies.
If you want to apply it to something production-oriented or even to keep your variables safe, the most recommended thing is to use an
.env
file, where you can either use theenv_file
tag in your docker-compose or pass the variables using${variable name}
.I recommend taking a look at this documentation here:
docs.docker.com/compose/environmen...
You can also use docker secrets (the most recommended):
docs.docker.com/engine/swarm/secrets/
docs.docker.com/compose/use-secrets/