Docker Compose With a Database
Applications being built to run in containers can have a database included in the stack. It creates a better experience for someone who just wants to try the application after finding it on a GitHub repository… or for quality control engineers to run tests against a blank slate.
We typically specify a connection string or hostname to the database within our applications, through a config file or environment variable. When using Docker Compose, the hostname of the database server will be the name of the service set by us in the docker-compose.yml; this makes the hostname of the database server predictable, simplifying deployment, and also gives us the ability to run multiple database servers.
Let’s take MongoDB as an example - we can use a connection string URI like this:
mongodb://myroot:notmypassword@mongo
mongo
is the database hostname, defined by the service name in the docker-compose.yml file (to be created further in this blog post).
We can create a Docker file (named Dockerfile
by convention, but you can name it anything you want - use the default name to keep things simple) for the application (or use a predefined image, if all we want to do is run somebody else’s container image). Here, let’s use a Ruby on Rails application as an example:
FROM ruby:3.2.0
EXPOSE 3000
COPY /projects/src /usr/src/app RUN bundle install
CMD ["rails", "server"]
Then, we create the Docker Compose file (named docker-compose.yml
):
version: '3.1'
services:
mongo:
image: mongo:6.0.3
environment:
MONGO_INITDB_ROOT_USERNAME: myroot
MONGO_INITDB_ROOT_PASSWORD: notmypassword
volumes:
- ./db:/data/db
myapp:
build: .
ports:
- "3000:3000"
The database ports can also be mapped to make it accessible from the host, similar to the myapp
service in the example above, to make it available to client tools such as MongoDB Compass, MongoDB shell, Studio 3T, NoSQLBooster or other similar tools. The volume is specified to make the data persistent; if we want to start with a clean state each time, we can omit it for the database server to use ephemeral storage instead.
Finally, we start our stack with:
mkdir db # The directory to store our database files
docker-compose build # Ensures the app image is rebuit
docker-compose up
The containers can be stopped with the command “docker-compose stop”, and the stopped containers can be removed with the command “docker-compose rm”.
Notes
If we were to start the database container manually, this would be the equivalent docker run
command:
docker run -d -p 27017:27017 --name mongod01 -e MONGO_INITDB_ROOT_USERNAME=myroot -e MONGO_INITDB_ROOT_PASSWORD=notmypassword -v "./mdb:/data/db" mongo:latest