skip to Main Content

I’m currently creating a backend API using Java 20, and Springboot. Im kind of a beginner. So Ill explain the structure Im following. Before thinking about dockerizing the application, I ussed a docker-compose file to run a PostgreSQL Database.

docker-compose.yaml


services:
  db:
    container_name: postgres
    image: postgres
    environment:
      POSTGRES_USER: redacted
      POSTGRES_PASSWORD: redacted
      PGDATA: /data/postgres
    volumes:
      - db:/data/postgres
    ports:
      - "5332:5432"
    networks:
      - db
    restart: unless-stopped

networks:
  db:
    driver: bridge

volumes:
  db:

And here is my application.yaml file as well

server:
  port: 8080

spring:
  datasource:
    url: jdbc:postgresql://db:5332/irrigation
    username: redacted
    password: redacted
    data: classpath:data.sql
  jpa:
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
        format-sql: true
    show-sql: false
  main:
    web-application-type: servlet

Initially, i ran docker-compose up -d to start up the postgre container on my machine (Im using a MacBook M1 Pro). Its working perfectly on my local machine.
And here is the structure of my project (if It helps in anyways)

.
├── Dockerfile
├── HELP.md
├── docker-compose.yaml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── amr
│       │           └── irrigation
│       │               ├── Main.java
│       │               ├── Plot.java
│       │               ├── TimeSlot.java
│       │               ├── controllers
│       │               │   ├── PlotController.java
│       │               │   └── TimeslotController.java
│       │               └── repositories
│       │                   ├── PlotRepository.java
│       │                   └── TimeslotRepository.java
│       └── resources
│           ├── application.yaml
│           ├── data.sql
│           └── import.sql
└── target
    ├── classes
    │   ├── application.yaml
    │   ├── com
    │   │   └── amr
    │   │       └── irrigation
    │   │           ├── Main.class
    │   │           ├── Plot.class
    │   │           ├── TimeSlot.class
    │   │           ├── controllers
    │   │           │   ├── PlotController.class
    │   │           │   └── TimeslotController.class
    │   │           └── repositories
    │   │               ├── PlotRepository.class
    │   │               └── TimeslotRepository.class
    │   ├── data.sql
    │   └── import.sql
    ├── generated-sources
    │   └── annotations
    ├── irrigatiosvc-0.0.1-SNAPSHOT.jar
    ├── irrigatiosvc-0.0.1-SNAPSHOT.jar.original
    ├── maven-archiver
    │   └── pom.properties
    └── maven-status
        └── maven-compiler-plugin
            └── compile
                └── default-compile
                    ├── createdFiles.lst
                    └── inputFiles.lst

The application runs perfectly. I’ve also used the ./mvnw command to build a jar file for my application and I ran it. It works as planned.

What I actually want to do
Is to have my application as a microservice that runs on its own. To be able to run calls on my APIs using postman on localhost:8080, and to have the database running on port 5332. I also want the container to be self-sufficient, or in other words, have all the dependencies it needs to run on any machine.
EDIT: I also want to run a command that created a database called irrigation. As this is the second thing I did manually. The tables, as I assume would happend, supposedly will be created automatically using the import.sql file I have.

What the problem is

Here is my docker file

FROM eclipse-temurin:20.0.1_9-jdk-alpine
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
EXPOSE 8080
EXPOSE 5332
EXPOSE 5432
COPY src ./src

RUN apk add docker-compose
##CMD ["./mvnw", "spring-boot:run"]

CMD ["./mvnw", "spring-boot:run"]

This builds successfully. However, I get faced with an error

Caused by: org.postgresql.util.PSQLException: Connection to localhost:5332 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.

COMPLETE LOG FILE: https://pastebin.com/shhtci33
PS: Im not sure if I should paste the log file here or just link it one pastebin. Please correct me if Im wrong.

I have tried for countless hours, and searched a lot. I still dont know what the problem is. I tried running the docker-compose up -d on the dockerfile initially, however, It just returned docker-compose now found. I’d be very glad if someone with more experience can spot where the error is, and potentially explain to me briefly how to solve it. Thank you for reading my question! 🙂

2

Answers


  1. In application.yaml, jdbc url cannot be localhost:5332 because spring application is running on one container and postgres db is on another container. Instead of localhost, it should point hostname of postgres container.

    For example, create a container_name for postgres, create a environment variable and use that in jdbc url.
    Refer

    https://github.com/wkrzywiec/kanban-board/blob/master/kanban-app/src/main/resources/application.properties

    https://github.com/wkrzywiec/kanban-board/blob/master/docker-compose.yml

    Login or Signup to reply.
  2. Your application is trying to connect to a PostgreSQL server running on localhost, but when your application is running inside a Docker container, localhost refers to the container itself, not your machine.

    In your Docker Compose file, you’ve correctly set up a PostgreSQL server as a service and named it db. So you should configure your Spring Boot application to connect to this server by using the service name db as the hostname, instead of localhost.

    However, when you’re running your application inside a Docker container, you aren’t actually using Docker Compose, which is why your application isn’t able to connect to the database.

    You need to modify your docker-compose.yaml file to include the application as a service, alongside the database service, and use Docker Compose to run both services. Here is an example of what that might look like:

    version: '3'
    
    services:
      app:
        container_name: irrigation-app
        build:
          context: .
          dockerfile: Dockerfile
        depends_on:
          - db
        ports:
          - "8080:8080"
    
      db:
        container_name: postgres
        image: postgres
        environment:
          POSTGRES_USER: redacted
          POSTGRES_PASSWORD: redacted
          PGDATA: /data/postgres
        volumes:
          - db:/data/postgres
        ports:
          - "5332:5432"
        networks:
          - db
        restart: unless-stopped
    
    networks:
      db:
        driver: bridge
    
    volumes:
      db:
    

    You should also modify your application.yaml file to connect to the db host instead of localhost:

    spring:
      datasource:
        url: jdbc:postgresql://db:5432/irrigation
    

    Now, you should be able to start your application and database with the docker-compose up command, and your application should be able to connect to the database.

    Note: Docker Compose sets up a default network for all services defined in a docker-compose.yaml file and service-to-service communication can be done via service names as hostnames. Hence db is used as the hostname in the JDBC connection string.

    Also, keep in mind that Docker Compose starts services in dependency order, which in your case means it starts the database before the application. However, it does not wait for a service to be "ready" (such as a database fully starting up and being ready to accept connections) before starting the next one. To handle this, your application should be able to tolerate the database connection not being immediately available, and should retry the connection if the initial connection attempt fails. This could be handled by Spring Boot’s spring.datasource.initialization-mode property set to always (it is embedded by default).

    Last but not least, I removed EXPOSE 5332 and EXPOSE 5432 from your Dockerfile as your app container doesn’t need to expose these ports, and updated the PostgreSQL port in application.yaml to 5432 because inside the network created by Docker Compose, the PostgreSQL container will be listening on its original port (5432), not the mapped one (5332).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search