Docker-compose to the rescue

Ewere Ebie
4 min readMay 11, 2023

Creating containers using native Docker commands, as we demonstrated in our previous article, gives a solid understanding of how Docker works. However, as service requirements increase, it can become cumbersome to manage them in this manner.

Docker Compose simplifies the description of services, volumes, and networks in a YAML file, allowing us to manage application lifecycles, including image building and container execution.

Our goal today is to create the docker resources needed for our Bambi event manager app using docker compose. Let’s go! 😤

Prerequisites

To complete this lesson you should have

Step 1 — Pull down application

Open your terminal on the folder you wish to have this repository and pull down the repo with these commands

git clone https://github.com/caleb-42/event-manager-node.git
cd event-manager-node
git checkout use-docker-compose

once it’s complete open the folder with your code editor.

Step 2 — Create .env file using env-sample file

There is a file named env-sample in the project directory. Use its content to create a .env file for the event application.

PORT=3210
NODE_ENV=development
API_KEY=myapikey
DATABASE_URL=postgres://postgres:password@postgres-db/event_manager
JWT_SECRET=secret

Step 3 — Create Docker compose file

Create a file called docker-compose.yml at the root of your application and paste in the yaml below .

version: "3.4"
services:
event-manager:
container_name: event-manager-app
env_file: ./.env
build:
context: .
dockerfile: Dockerfile
ports:
- "3210:3210"
networks:
- event-manager-network
networks:
event-manager-network:
driver: bridge

version defines the docker-compose release that supports the schema in the file, while services is a top level property that has listing of containers you plan to run. networks property defines a bridge network called event-manager-network which every container will get added to to enable communication.

Docker compose creates a bridge network by default that enable communication between containers. The networks property allows us create our own custom networks.

One of the ways to pass environment variables to a container is to use the env_file property. we assign .env file we created previously to it.

The build property specifies the context(folder location) to find the Docker build file and the dockerfile property states the file name. The port key maps a host port(3210) to our container port(3210).

Step 4 — Add postgres-db to docker-compose file

Before running our application we need to include our Postgres database so the event manager can persist data. Now your docker-compose.yml file should look like this 👇.

version: "3.4"
services:
event-manager:
container_name: event-manager-app
env_file: ./.env
build:
context: .
dockerfile: Dockerfile
ports:
- "3210:3210"
networks:
- event-manager-network
postgres-db:
container_name: event-manager-db
image: postgres:14.1-alpine
restart: always
environment:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "password"
POSTGRES_DB: "event_manager"
ports:
- "5434:5432"
volumes:
- db:/var/lib/postgresql/data
- ./db.sql:/docker-entrypoint-initdb.d/db.sql
networks:
- event-manager-network
volumes:
db:
driver: local
networks:
event-manager-network:
driver: bridge

Docker compose will pull an image from Dockerhub using the value given to the image key. The restart key with a value of “always” means that if the container stops arbitrarily docker compose will spin up a new one.

Using environment key allows us pass environment variables to a container using key-value pairs. The variables of importance to the postgres-db container are the POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB.

While sufficient for local development, it is not recommended to pass sensitive information plainly to your Postgres container in production.

the port key maps a host port(5434) to our container port(5432) granting us the flexibility of escaping port conflict errors.

We use the volumes key to mount a db volume to the /var/lib/postgresql/data location in the container so we can persist the data.
The db.sql initialisation script is mounted to the docker-entrypoint-initdb.d location in our container so it runs the moment the container starts.

Lastly we add the postgres-db to the event-manager-network.

Step 5 — Build Docker Images with compose

With the docker-compose file ready we can now build our image with the command below

docker-compose build

The outcome of a successful build should be the image below.

Step 5 — Run Docker container with compose

The last step is to create a container using our image. Use the command below to do so.

docker-compose up

The outcome of a successful build should be the image below.

Now if you navigate to http://localhost:3210 you will see the app running and data displayed.

Conclusion

By utilising Docker Compose, we effectively managed our services without the need for repetitive Docker commands on the terminal. In my upcoming article, I’ll delve into shelling into containers and handling logs for debugging purposes. Stay tuned and see you next time!

I’m glad you took the time to read my blog. If you liked it leave a clap and follow my medium account for more content like this. Thanks 👋.

--

--

Ewere Ebie

I write because it’s less exhausting than speaking. And its fun