1

I am building a Spring Boot application which uses PostgreSQL with docker-compose. When I run my containers using docker-compose up --build, my Spring Boot application fails to start because it does not find the PostgreSQL container's hostname.

Spring Boot Dockerfile

FROM maven:3.6.3-openjdk-14-slim AS build  
COPY src /usr/src/app/src  
COPY pom.xml /usr/src/app  
RUN mvn -f /usr/src/app/pom.xml clean package

FROM openjdk:14-slim
COPY --from=build /usr/src/app/target/server-0.0.1-SNAPSHOT.jar /usr/app/server-0.0.1-SNAPSHOT.jar
EXPOSE 9000  
ENTRYPOINT ["java","-jar","/usr/app/server-0.0.1-SNAPSHOT.jar"]

docker-compose.yml

version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: my_db
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - db-network
    restart: always

  server:
    build: './server'
    depends_on:
      - db
    restart: always
    ports:
      - "9000:9000"
    networks:
      - db-network
    volumes:
      - ./server:/server


networks:
  db-network:

volumes:
  db-data:

application.properties

spring.datasource.url=jdbc:postgresql://db:5432/my_db
spring.datasource.username=postgres
spring.datasource.password=postgres

Error output

Caused by: java.net.UnknownHostException: db

My guess is that docker-compose's virtual network isn't created yet during the build stage of the Spring Boot Dockerfile. Any idea on how to solve this issue ?

Skeird
  • 173
  • 2
  • 10

3 Answers3

1

Lots of info here: https://docs.docker.com/compose/networking/

Within the web container, your connection string to db would look like postgres://db:5432, and from the host machine, the connection string would look like postgres://{DOCKER_IP}:8001.

What this is saying is db:5432 is fine to use within docker-compose.yaml and the IP address will be passed (not "db"), but using it externally within your application code isn't going to work. You could however pass from docker-compose.yaml db as an application input variable, which your application could fill in in the configuration file. This would enable you then to connect.

Externalising configuration like this is fairly common practice so should be a relatively easy fix.

eg:

Docker-compose.yaml

version: '3'

services:
  db:
    container_name: db
    image: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: my_db
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - db-network
    restart: always

  server:
    build: './server'
    depends_on:
      - db
    environment:
      DB_HOST: db # untested, but there should be a way to pass this in
      DB_PORT: 5432
      DB_DATABASE: my_db
      DB_USER: postgres
      DB_PASSWORD: postgres
    restart: always
    ports:
      - "9000:9000"
    networks:
      - db-network
    volumes:
      - ./server:/server


networks:
  db-network:

volumes:
  db-data:

Then have an application.properties file located under src/main/java/resources/application.properties

spring.datasource.url=jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
Rob Evans
  • 2,822
  • 1
  • 9
  • 15
  • I tried your solution, but the evironment variable isn't even loaded. I get the error `java.net.UnknownHostException: ${DB_HOST}`, as if the application.properties did not replace `${DB_HOST}` with `db` – Skeird Oct 27 '20 at 15:35
  • is this within the container when spun up? You can check with `docker exec -it [containerId] bash` which gets you inside the container, then `env` to list the environment variables ... if `DB_HOST` isn't set correctly as an environment variable it won't be replaced in the application.properties file. Once you've verified its set the variable should cascade into your application – Rob Evans Oct 27 '20 at 15:40
  • copy the entire project to the image, add `WORKDIR /usr/src/app`, then you should be able to add the standard `RUN mvn clean package`. Unless there's an error in the `pom.xml` that command should run so long as the pom.xml is in the dir you're specifying. i.e. don't just do `COPY src /usr/src/app/src` but instead do `COPY . /usr/src/app` – Rob Evans Oct 27 '20 at 15:59
  • I've just checked and DB_HOST isn't set correctly which is weird because it is defined in my docker-compose – Skeird Oct 27 '20 at 16:02
  • you may need to supply `container_name: db` as part of the container definition in your `docker-compose.yaml` I've updated my `docker-compose.yaml` above with this change – Rob Evans Oct 27 '20 at 16:07
  • more info here: https://docs.docker.com/compose/networking/ – Rob Evans Oct 27 '20 at 16:11
  • You could also try setting it to localhost... if the two containers are on the same subnet/network this might work – Rob Evans Oct 27 '20 at 16:41
  • I managed to set DB_HOST in the container using args in the docker-compose. I still have the error `java.net.UnknownHostException: db` though. – Skeird Oct 27 '20 at 16:57
  • 1
    will try test this out my end tonight - shouldn't be too hard to get a minimal working version – Rob Evans Oct 27 '20 at 17:11
1

This post completely solved my issue. It turns out that maven was trying to establish the connection to the database while building the .jar file. All I had to do is modify my Dockerfile with this line RUN mvn -f /usr/src/app/pom.xml clean package -DskipTests.

Skeird
  • 173
  • 2
  • 10
0

Please do note while building the images the service will not have access to the database as it is not yet running . Only after the images are built and the containers are running do the services have access . So when you try to pass a host as db , it is not yet available in the build stage . It is available only once the db container starts running .

Mani Sagar
  • 76
  • 3