Vinícius A dos Santos

Vinícius A dos Santos

About MeEmail Me

E-commerce Backend - Monolith - Docker Compose to run the App and Infra locally - (Part 5)


This post is part of a series that already has:


So far we're running the App in Dev mode while leveraging Quarkus Dev Services. While running like that, the Quarkus platform runs containers for the supporting services. Those services are a Postgres database and a Redis server until this moment.

I want to run the App a bit closer to how it would be in production, but still locally. To accomplish that, I will leverage Docker Compose which as the name suggests, composes several services that compound a System.

Properties Setup

First, I needed to set up Postgres and Redis connection properties. I set them as prod, because by default when running a Quarkus App from its built .jar, with quarkus run, or from its native executable, it runs with the prod profile:

# The database type we're connecting to
# It's a good practice to use the same version of the database in development and production for leveraging the same features
%prod.quarkus.datasource.db-version = ${DATABASE_VERSION}
# Just to be safe that the database won't be dropped and recreated in production

Docker Compose

In order to make it more flexible, I've created two distinct docker compose files. One for the infrastructure and one for the App. This way, I can run all of them in docker or just the infra. If I want to run my App out of the docker daemon (with maven run, or from the .jar, for instance), I can run just the Postgres DB and Redis.

# infra/compose/

# The yaml format which is suitable for running with Docker Engine 1.13.0+
version: '3'
# The services composition name
name: ecommerce-monolith
    container_name: em-postgres
    image: postgres:13.2
      POSTGRES_USER: root
      POSTGRES_DB: postgres
      POSTGRES_PASSWORD: password
      PGDATA: /data/postgres
      - 5499:5432
      - ecommerce-network

    image: redis:6.2.1
      - 6399:6379
      - ecommerce-network

# A dedicated and isolated network for those services to communicate in bridge mode
    driver: bridge

# The yaml format which is suitable for running with Docker Engine 1.13.0+
version: '3'
# The services composition name
name: ecommerce-monolith
    # Use the app docker image built by Quarkus
    image: viniciussantos/ecommerce-monolith:1.0.0-SNAPSHOT
    # Redirect requests aiming locahost:8080 to the container 8080 port
      - 8080:8080
    # Defines the Environment Variables needed by the App to run
      DATABASE_PASSWORD: password
      # Note that I used the name of the DB service as the host, "localhost" would not work because we're inside the Docker Daemon here.
      DATABASE_HOST: em-postgres
      DATABASE_PORT: 5432
      DATABASE_NAME: postgres
      # Same as above
      REDIS_HOST: em-redis
      REDIS_PORT: 6379
    # Defines that the App should be started after those two. That's because the App needs those services running. But it's IMPORTANT to be aware that this only defines the start of em-app being triggered after the start of em-postgres and em-redis. Therefore, the App may start faster than the Postgres DB, causing the App to be unable to connect to the database. That would cause a start failure caused by the Flyway migrations check.
      - em-postgres
      - em-redis
      - ecommerce-network

Note the caveat of depends-on property. Fortunately, there are ways to force a service to start only after another one is ready.

To run the both the infra services and the App, we need to:

  1. build the App docker image by running the following command from the ecommerce-monolith folder: ./mvnw quarkus:image-build
  2. run the compose command: docker-compose -f infra/ -f infra/ That will combine both yaml files

If I only need the infra, it's just docker-compose -f infra/

We could set the image build in the file too. However, I chose to leverage the built-in Maven Quarkus Plugin command to build the App's image, because I will use that in my CI jobs later.


Now, I think that having to remember several steps for a specific action might be overwhelming. I aim for a single action: run my App which depends on a few infra services, but I need to remember to build the App's image, because I may have changed its code, and run docker-compose against 2 configuration files. We can simplify that and reduce things to be remembered by just running:


And here is what the script file looks like:


declare dc_infra=infra/
declare dc_app=infra/

# building the App container image
function build_app() {
    cd ecommerce-monolith
    ./mvnw quarkus:image-build -DskipTests
    cd ..

# Starting just the Infra
function start_infra() {
    echo "Starting infra..."
    docker-compose -f $dc_infra up -d

function stop_infra() {
    echo "Stopping infra..."
    docker-compose -f $dc_infra stop

# Starting just the Infra and App
function start() {
    echo "Starting all services..."
    docker-compose -f $dc_infra -f $dc_app up -d
    docker-compose -f $dc_infra -f $dc_app logs -f

function stop() {
    echo "Stopping all services..."
    docker-compose -f $dc_infra -f $dc_app stop
    docker-compose -f $dc_infra -f $dc_app rm -f

# Restarting (in case of changes in the App or in the yaml files)
function restart() {
    sleep 5

# Default action is to start Infra and App

if [ "$1" == "start" ]; then
elif [ "$1" == "stop" ]; then
elif [ "$1" == "restart" ]; then
elif [ "$1" == "start-infra" ]; then
elif [ "$1" == "stop-infra" ]; then
    echo "Invalid action. Use start, stop, restart, start-infra or stop-infra."
    exit 1

eval $action

As you can see in the script, we can also run:

  • ./ stop
  • ./ start_infra
  • ./ stop_infra
  • ./ restart


And that's it for this post. Thanks for reading.

Code here

← Back to home