Docker Compose: Spring Boot and Postgres example
Tags: webdev, devops, docker, java
Published: 2023-10-08 02:53:56

<a href="https://www.docker.com/">Docker</a> provides lightweight containers to run services in isolation from our infrastructure so we can deliver software quickly. In this tutorial, I will show you how to dockerize Spring Boot microservice and Postgres example using <a href="https://docs.docker.com/compose/">Docker Compose</a>.

Related Posts:
- <a href="https://www.bezkoder.com/spring-boot-postgresql-example/">Spring Boot and PostgreSQL CRUD example</a>
- <a href="https://www.bezkoder.com/spring-boot-r2dbc-postgresql/">Spring Boot R2DBC + PostgreSQL example</a>
- <a href="https://www.bezkoder.com/spring-boot-security-postgresql-jwt-authentication/">Spring Boot, Spring Security, PostgreSQL: JWT Authentication example</a>
- Documentation: <a href="https://www.bezkoder.com/spring-boot-swagger-3/">Spring Boot Swagger 3 example</a>
- Caching: <a href="https://www.bezkoder.com/spring-boot-redis-cache-example/">Spring Boot Redis Cache example</a>

<h2>Overview</h2>
Assume that we have a Spring Boot Application working with Postgres database.
The problem is to containerize a system that requires more than one Docker container:
<ul>
	<li>Spring Boot for Rest API</li>
	<li>Postgres for database</li>
</ul>

Docker Compose helps us setup the system more easily and efficiently than with only Docker. We're gonna following these steps:
<ul>
	<li>Create Spring Boot App working with Postgres database.</li>
	<li>Create Dockerfile for Spring Boot App.</li>
	<li>Write Docker Compose configurations in YAML file.</li>
	<li>Set Spring Boot Docker Compose Environment variables.</li>
	<li>Run the system.</li>
</ul>

Directory Structure:

<img src="https://www.bezkoder.com/wp-content/uploads/docker-compose-spring-boot-postgres.png" alt="docker-compose-spring-boot-postgres" width="180" height="160" class="alignnone size-full wp-image-15850" />

<h2>Create Spring Boot App</h2>
You can read and get Github source code from one of following tutorials:
- <a href="https://www.bezkoder.com/spring-boot-postgresql-example/">Spring Boot and PostgreSQL CRUD example</a>
- <a href="https://www.bezkoder.com/spring-boot-r2dbc-postgresql/">Spring Boot R2DBC + PostgreSQL example</a>
- <a href="https://www.bezkoder.com/spring-boot-security-postgresql-jwt-authentication/">Spring Boot, Spring Security, PostgreSQL: JWT Authentication example</a>

Using the code base above, we put the Spring Boot project in <strong>bezkoder-app</strong> folder without the need of <strong>resources</strong>/<em>application.properties</em>. It is because Environment variables will be exported to <em>.env</em> file.

<h2>Create Dockerfile for Spring Boot App</h2>
Dockerfile defines a list of commands that Docker uses for setting up the Spring Boot application environment. So we put the file in <strong>bezkoder-app</strong> folder.

Because we will use Docker Compose, we won't define all the configuration commands in this Dockerfile.

<strong>bezkoder-app</strong>/<em>Dockerfile</em>
```properties
# FROM maven:3.8.2-jdk-8 # for Java 8
FROM maven:3.8.5-openjdk-17

WORKDIR /bezkoder-app
COPY . .
RUN mvn clean install

CMD mvn spring-boot:run
```

Let me explain some points:
<ul>
	<li><code>FROM</code>: install the image of the Maven - JDK version.</li>
	<li><code>WORKDIR</code>: path of the working directory.</li>
	<li><code>COPY</code>: copy all the files inside the project directory to the container.</li>
	<li><code>RUN</code>: execute a command-line inside the container: <code>mvn clean install</code> to install the dependencies in <em>pom.xml</em>.</li>
	<li><code>CMD</code>: run script <code>mvn spring-boot:run</code> after the image is built.</li>
</ul>

<h2>Write Docker Compose configurations</h2>
On the root of the project directory, we're gonna create the <em>docker-compose.yml</em> file. Follow <a href="https://docs.docker.com/compose/compose-file/compose-file-v3/">version 3</a> syntax defined by Docker:

```properties
version: '3.8'

services: 
    postgresdb:
    app:

volumes:
```
<ul>
	<li><code>version</code>: Docker Compose file format version will be used.</li>
	<li><code>services</code>: individual services in isolated containers. Our application has two services: <code>app</code> (Spring Boot) and <code>postgresdb</code> (PostgreSQL database).</li>
	<li><code><a href="https://docs.docker.com/storage/volumes/">volumes</a></code>: named volumes that keeps our data alive after restart.</li>
</ul>

Let's implement the details.

<em>docker-compose.yml</em>
```properties
version: "3.8"

services:
  postgresdb:
    image: postgres
    restart: unless-stopped
    env_file: ./.env
    environment:
      - POSTGRES_USER=$POSTGRESDB_USER
      - POSTGRES_PASSWORD=$POSTGRESDB_ROOT_PASSWORD
      - POSTGRES_DB=$POSTGRESDB_DATABASE
    ports:
      - $POSTGRESDB_LOCAL_PORT:$POSTGRESDB_DOCKER_PORT
    volumes:
      - db:/var/lib/postgres
  app:
    depends_on:
      - postgresdb
    build: ./bezkoder-app
    restart: on-failure
    env_file: ./.env
    ports:
      - $SPRING_LOCAL_PORT:$SPRING_DOCKER_PORT
    environment:
      SPRING_APPLICATION_JSON: '{
        "spring.datasource.url"  : "jdbc:postgresql://postgresdb:$POSTGRESDB_DOCKER_PORT/$POSTGRESDB_DATABASE",
        "spring.datasource.username" : "$POSTGRESDB_USER",
        "spring.datasource.password" : "$POSTGRESDB_ROOT_PASSWORD",
        "spring.jpa.properties.hibernate.dialect" : "org.hibernate.dialect.PostgreSQLDialect",
        "spring.jpa.hibernate.ddl-auto" : "update"
      }'
    volumes:
      - .m2:/root/.m2
    stdin_open: true
    tty: true

volumes:
  db:
```

- <strong>postgresdb</strong>:
<ul>
	<li><code>image</code>: official Docker image</li>
	<li><code>restart</code>: configure the <a href="https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy">restart policy</a></li>
	<li><code>env_file</code>: specify our <em>.env</em> path that we will create later</li>
	<li><code>environment</code>: provide setting using environment variables</li>
	<li><code>ports</code>: specify ports will be used</li>
	<li><code>volumes</code>: map volume folders</li>
</ul>

- <strong>app</strong>:
<ul>
	<li><code><a href="https://docs.docker.com/compose/compose-file/compose-file-v3/#depends_on">depends_on</a></code>: dependency order, <strong>postgresdb</strong> is started before <strong>app</strong></li>
	<li><code>build</code>: configuration options that are applied at build time that we defined in the <em>Dockerfile</em> with relative path</li>
	<li><code>environment</code>: environmental variables that Spring Boot application uses</li>
	<li><code>stdin_open</code> and <code>tty</code>: keep open the terminal after building container</li>
</ul>

You should note that the host port (<code>LOCAL_PORT</code>) and the container port (<code>DOCKER_PORT</code>) is different. Networked service-to-service communication uses the container port, and the outside uses the host port.

<h2>Docker Compose Environment variables</h2>
In the service configuration, we used environmental variables defined inside the <em>.env</em> file. Now we start writing it.

<em>.env</em>
```properties
POSTGRESDB_USER=postgres
POSTGRESDB_ROOT_PASSWORD=123456
POSTGRESDB_DATABASE=bezkoder_db
POSTGRESDB_LOCAL_PORT=5433
POSTGRESDB_DOCKER_PORT=5432

SPRING_LOCAL_PORT=6868
SPRING_DOCKER_PORT=8080
```

<h2>Run the Spring Boot microservice with Docker Compose</h2>
We can easily run the whole with only a single command:
<code>docker compose up</code>

Docker will pull the PostgreSQL and Maven images (if our machine does not have it before).

The services can be run on the background with command:
<code>docker compose up -d</code>

```bash
$ docker compose up -d
[+] Running 14/14
 ✔ postgresdb 13 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                                                                                                                               30.0s 
   ✔ a803e7c4b030 Pull complete                                                                                                                                                                                               
   ✔ 009c876521a0 Pull complete                                                                                                                                                                                               
   ✔ 9c412905cca2 Pull complete                                                                                                                                                                                               
   ✔ 6463d4bf467a Pull complete                                                                                                                                                                                              
   ✔ bd8b983728ed Pull complete                                                                                                                                                                                              
   ✔ febc167f3560 Pull complete                                                                                                                                                                                              
   ✔ d73c81c4ade3 Pull complete                                                                                                                                                                                              
   ✔ 34b3b0ac6e9e Pull complete                                                                                                                                                                                              
   ✔ 9bd86d074f4e Pull complete                                                                                                                                                                                              
   ✔ 406f63329750 Pull complete                                                                                                                                                                                              
   ✔ ec40772694b7 Pull complete                                                                                                                                                                                              
   ✔ 7d3dfa1637e9 Pull complete                                                                                                                                                                                              
   ✔ e217ca41159f Pull complete                                                                                                                                                                                              
[+] Building 2.9s (9/9) FINISHED                                                                                                                                                                                    
 => [app internal] load build definition from Dockerfile                                                                                                                                                                      
 => => transferring dockerfile: 231B                                                                                                                                                                                          
 => [app internal] load .dockerignore                                                                                                                                                                                         
 => => transferring context: 2B                                                                                                                                                                                               
 => [app internal] load metadata for docker.io/library/maven:3.8.5-openjdk-17                                                                                                                                                 
 => [app 1/4] FROM docker.io/library/maven:3.8.5-openjdk-17@sha256:3a9c30b3af6278a8ae0007d3a3bf00fff80ec3ed7ae4eb9bfa1772853101549b                                                                                           
 => [app internal] load build context                                                                                                                                                                                         
 => => transferring context: 1.94kB                                                                                                                                                                                           
 => [app 2/4] WORKDIR /bezkoder-app                                                                                                                                                                                    
 => [app 3/4] COPY . .                                                                                                                                                                                                 
 => [app 4/4] RUN mvn clean install                                                                                                                                                                                    
 => [app] exporting to image                                                                                                                                                                                                  
 => => exporting layers                                                                                                                                                                                                       
 => => writing image sha256:bb65745b652009fa4bf508b771c9abc5e90f257f9efde63251df8c0a74c672d7                                                                                                                                  
 => => naming to docker.io/library/spring-boot-postgres-app                                                                                                                                                                   
[+] Running 2/1
 ✔ Container spring-boot-postgres-postgresdb-1  Created                                                                                                                                                                       
 ✔ Container spring-boot-postgres-app-1         Created                                                                                                                                                                       
Attaching to spring-boot-postgres-app-1, spring-boot-postgres-postgresdb-1
...
spring-boot-postgres-postgresdb-1  | PostgreSQL init process complete; ready for start up.
spring-boot-postgres-postgresdb-1  | 
spring-boot-postgres-postgresdb-1  | 
spring-boot-postgres-postgresdb-1  | 2023-10-03 03:35:18.102 UTC [1] LOG:  starting PostgreSQL 16.0 (Debian 16.0-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
spring-boot-postgres-postgresdb-1  | 2023-10-03 03:35:18.102 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
spring-boot-postgres-postgresdb-1  | 2023-10-03 03:35:18.102 UTC [1] LOG:  listening on IPv6 address "::", port 5432
spring-boot-postgres-postgresdb-1  | 2023-10-03 03:35:18.105 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
spring-boot-postgres-postgresdb-1  | 2023-10-03 03:35:18.109 UTC [64] LOG:  database system was shut down at 2023-10-03 03:35:18 UTC
spring-boot-postgres-postgresdb-1  | 2023-10-03 03:35:18.113 UTC [1] LOG:  database system is ready to accept connections
spring-boot-postgres-app-1         | [INFO] Scanning for projects...
spring-boot-postgres-app-1         | [INFO] 
spring-boot-postgres-app-1         | [INFO] --------------< com.bezkoder:spring-boot-jpa-postgresql >---------------
spring-boot-postgres-app-1         | [INFO] Building spring-boot-jpa-postgresql 0.0.1-SNAPSHOT
spring-boot-postgres-app-1         | [INFO] --------------------------------[ jar ]---------------------------------
spring-boot-postgres-app-1         | [INFO] 
spring-boot-postgres-app-1         | [INFO] >>> spring-boot-maven-plugin:3.1.0:run (default-cli) > test-compile @ spring-boot-jpa-postgresql >>>
...
spring-boot-postgres-app-1         | 2023-10-03T03:41:46.545Z  INFO 59 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
spring-boot-postgres-app-1         | 2023-10-03T03:41:46.551Z  INFO 59 --- [           main] s.j.p.SpringBootJpaPostgresqlApplication : Started SpringBootJpaPostgresqlApplication in 2.188 seconds (process running for 2.354)
```

Now you can check the current working containers:

```bash
$ docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED         STATUS         PORTS                                       NAMES
9c3d10422b09   spring-boot-postgres-app   "/usr/local/bin/mvn-…"   2 minutes ago   Up 2 minutes   0.0.0.0:6868->8080/tcp, :::6868->8080/tcp   spring-boot-postgres-app-1
ed0b41f7f1c8   postgres                   "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   0.0.0.0:5433->5432/tcp, :::5433->5432/tcp   spring-boot-postgres-postgresdb-1
```

And Docker images:

```bash
$ docker images
REPOSITORY                 TAG            IMAGE ID       CREATED         SIZE
spring-boot-postgres-app   latest         bb65745b6520   5 minutes ago   962MB
postgres                   latest         2d74f8a2591c   6 minutes ago   417MB
```

Send a HTTP request to the Spring Boot - Postgres system:

<img src="https://www.bezkoder.com/wp-content/uploads/docker-compose-spring-boot-postgres-test-api.png" alt="docker-compose-spring-boot-postgres-test-api" width="600" height="420" class="alignnone size-full wp-image-15849" />

Check Postgres Database:

```bash
$ docker exec -ti spring-boot-postgres-postgresdb-1 /bin/bash
root@8869979fbd27:/# psql bezkoder_db postgres
psql (16.0 (Debian 16.0-1.pgdg120+1))
Type "help" for help.

bezkoder_db=# SELECT * FROM tutorials;
 id |    description    | published |                title                 
----+-------------------+-----------+--------------------------------------
  1 | Tut#1 Description | f         | bezkoder Docker Spring Boot Postgres
(1 row)
```

<h2>Stop the Application</h2>
Stopping all the running containers is also simple with a single command:
<code>docker compose down</code>

```bash
$ docker compose down
 ✔ Container spring-boot-postgres-app-1         Removed                                                                                                                                                      
 ✔ Container spring-boot-postgres-postgresdb-1  Removed                                                                                                                                                      
 ✔ Network spring-boot-postgres_default         Removed     
```

If you need to stop and remove all containers, networks, and all images used by any service in <em>docker-compose.yml</em> file, use the command:
<code>docker compose down --rmi all</code>

<h2>Conclusion</h2>
Today we've successfully created Docker Compose file for Spring Boot application and Postgres. Now we can connect Spring Boot to PostgreSQL with Docker on a very simple way: <em>docker-compose.yml</em>.

You can apply this way to one of following project:
- <a href="https://www.bezkoder.com/spring-boot-postgresql-example/">Spring Boot and PostgreSQL CRUD example</a>
- <a href="https://www.bezkoder.com/spring-boot-r2dbc-postgresql/">Spring Boot R2DBC + PostgreSQL example</a>
- <a href="https://www.bezkoder.com/spring-boot-security-postgresql-jwt-authentication/">Spring Boot, Spring Security, PostgreSQL: JWT Authentication example</a>

If you want to deploy the system on AWS, please visit:
<a href="https://www.bezkoder.com/deploy-spring-boot-aws-eb/">Deploy Spring Boot App on AWS – Elastic Beanstalk</a>

Happy Learning! See you again.

<h2>Source Code</h2>
The source code for this tutorial can be found at <a href="https://github.com/bezkoder/docker-compose-spring-boot-postgres">Github</a>.

Documentation: <a href="https://www.bezkoder.com/spring-boot-swagger-3/">Spring Boot + Swagger 3 example (with OpenAPI 3)</a>
Caching: <a href="https://www.bezkoder.com/spring-boot-redis-cache-example/">Spring Boot Redis Cache example</a>