Installing Docker container for Java Spring Postgres application. Connection to localhost:5432 refused

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

I am trying to deploy my first docker container. It is a simple Java Spring REST API application.
It runs fine in Eclipse. I can connect to the database using SQL Explorer just fine.
I tried Telnet and Test-NetConnection -ComputerName localhost -Port 5432 in Powershell and it returns

ComputerName : localhost RemoteAddress : ::1 RemotePort :
5432 InterfaceAlias : Loopback Pseudo-Interface 1 SourceAddress :
::1 TcpTestSucceeded : True

Using JDK 17. Postgres 16. OS: Windows 10. Postgres service is running fine.

I tried everything I can find but I cannot make it succeed. I am 100% new to docker.

Docker fails after running: docker compose up --build

Full Exception:

org.postgresql.util.PSQLException: Connection to localhost:5432
refused. Check that the hostname and port are correct and that the
postmaster is accepting TCP/IP connections. server-1 | at
org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:342)
~[postgresql-42.6.2.jar:42.6.2] server-1 | at
org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:54)
~[postgresql-42.6.2.jar:42.6.2] server-1 | at
org.postgresql.jdbc.PgConnection.(PgConnection.java:263)
~[postgresql-42.6.2.jar:42.6.2] server-1 | at
org.postgresql.Driver.makeConnection(Driver.java:443)
~[postgresql-42.6.2.jar:42.6.2] server-1 | at
org.postgresql.Driver.connect(Driver.java:297)
~[postgresql-42.6.2.jar:42.6.2] server-1 | at
com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
~[HikariCP-5.0.1.jar:na] server-1 | at
com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:359)
~[HikariCP-5.0.1.jar:na] server-1 | at
com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:201)
~[HikariCP-5.0.1.jar:na] server-1 | at
com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:470)
~[HikariCP-5.0.1.jar:na] server-1 | at
com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:561)
~[HikariCP-5.0.1.jar:na] server-1 | at
com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:100)
~[HikariCP-5.0.1.jar:na] server-1 | at
com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
~[HikariCP-5.0.1.jar:na] server-1 | at
org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:428)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:61)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:276)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:107)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:68)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.boot.model.relational.Database.(Database.java:45)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:223)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:191)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:170)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1432)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1503)
~[hibernate-core-6.4.4.Final.jar:6.4.4.Final] server-1 | at
org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75)
~[spring-orm-6.1.5.jar:6.1.5] server-1 | at
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390)
~[spring-orm-6.1.5.jar:6.1.5] server-1 | at
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
~[spring-orm-6.1.5.jar:6.1.5] server-1 | at
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
~[spring-orm-6.1.5.jar:6.1.5] server-1 | at
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366)
~[spring-orm-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1833)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
~[spring-beans-6.1.5.jar:6.1.5] server-1 | at
org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1234)
~[spring-context-6.1.5.jar:6.1.5] server-1 | at
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952)
~[spring-context-6.1.5.jar:6.1.5] server-1 | at
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624)
~[spring-context-6.1.5.jar:6.1.5] server-1 | at
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
~[spring-boot-3.2.4.jar:3.2.4] server-1 | at
org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
~[spring-boot-3.2.4.jar:3.2.4] server-1 | at
org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
~[spring-boot-3.2.4.jar:3.2.4] server-1 | at
org.springframework.boot.SpringApplication.run(SpringApplication.java:334)
~[spring-boot-3.2.4.jar:3.2.4] server-1 | at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1354)
~[spring-boot-3.2.4.jar:3.2.4] server-1 | at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
~[spring-boot-3.2.4.jar:3.2.4] server-1 | at
com.robbank.crudApis.RobbankCrudApiApplication.main(RobbankCrudApiApplication.java:10)
~[classes/:0.0.1-SNAPSHOT] server-1 | at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
Method) ~[na:na] server-1 | at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown
Source) ~[na:na] server-1 | at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown
Source) ~[na:na] server-1 | at
java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
server-1 | at
org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91)
~[file:/:na] server-1 | at
org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53)
~[file:/:na] server-1 | at
org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58)
~[file:/:na]

Compose.yaml (auto generated):

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
  
# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
  server:
    build:
      context: .
    ports:
      - 8080:8080
  
# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker-compose up`.
  db:
    image: postgres
    restart: always
    ports:
      - "5432:5432"  # Map container port 5432 to host port 5432
    environment:
      POSTGRES_DB: robbankdb
      POSTGRES_USER: postgres  # Set the superuser username
      POSTGRES_PASSWORD: Password01  # Set the superuser password
    volumes:
      - db-data:/var/lib/postgresql/data 
    expose:
      - 5432
    healthcheck:
      test: [ "CMD", "pg_isready" ]
      interval: 10s
      timeout: 5s
      retries: 5
  
volumes:
  db-data:  # Define a named volume for database data persistence

application.properties:

spring.application.name=RobbankCrudApi
  
spring.datasource.url=jdbc:postgresql://localhost:5432/robbankdb
spring.datasource.username=robbankuser
spring.datasource.password=rob123
  
spring.jpa.hibernate.ddl-auto=create-drop
  
spring.jpa.show-sql=true 
  
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.default_schema=public
  
spring.jackson.serialization.indent-output=true
  
server.error.include-message=always 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.2.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.robbank.crudApis</groupId>
	<artifactId>RobbankCrudApi</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>RobbankCrudApi</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- dependency>
    		<groupId>org.flywaydb</groupId>
    		<artifactId>flyway-core</artifactId>
    	</dependency -->
	</dependencies>
  
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
  
</project>

Dockerfile (auto generated)

# syntax=docker/dockerfile:1
  
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/
  
# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7
  
################################################################################
  
# Create a stage for resolving and downloading dependencies.
FROM eclipse-temurin:17-jdk-jammy as deps
  
WORKDIR /build
  
# Copy the mvnw wrapper with executable permissions.
COPY --chmod=0755 mvnw mvnw
COPY .mvn/ .mvn/
  
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.m2 so that subsequent builds don't have to
# re-download packages.
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
    --mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests
  
################################################################################
  
# Create a stage for building the application based on the stage with downloaded dependencies.
# This Dockerfile is optimized for Java applications that output an uber jar, which includes
# all the dependencies needed to run your app inside a JVM. If your app doesn't output an uber
# jar and instead relies on an application server like Apache Tomcat, you'll need to update this
# stage with the correct filename of your package and update the base image of the "final" stage
# use the relevant app server, e.g., using tomcat (https://hub.docker.com/_/tomcat/) as a base image.
FROM deps as package
  
WORKDIR /build
  
COPY ./src src/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
    --mount=type=cache,target=/root/.m2 \
    ./mvnw package -DskipTests && \
    mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar
  
################################################################################
  
# Create a stage for extracting the application into separate layers.
# Take advantage of Spring Boot's layer tools and Docker's caching by extracting
# the packaged application into separate layers that can be copied into the final stage.
# See Spring's docs for reference:
# https://docs.spring.io/spring-boot/docs/current/reference/html/container-images.html
FROM package as extract
  
WORKDIR /build
  
RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted
  
################################################################################
  
# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the install or build stage where the necessary files are copied
# from the install stage.
#
# The example below uses eclipse-turmin's JRE image as the foundation for running the app.
# By specifying the "17-jre-jammy" tag, it will also use whatever happens to be the
# most recent version of that tag when you build your Dockerfile.
# If reproducability is important, consider using a specific digest SHA, like
# eclipse-temurin@sha256:99cede493dfd88720b610eb8077c8688d3cca50003d76d1d539b0efc8cca72b4.
FROM eclipse-temurin:17-jre-jammy AS final
  
# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    appuser
USER appuser
  
# Copy the executable from the "package" stage.
COPY --from=extract build/target/extracted/dependencies/ ./
COPY --from=extract build/target/extracted/spring-boot-loader/ ./
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
COPY --from=extract build/target/extracted/application/ ./
  
EXPOSE 8080
  
ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]

Use the service name instead of localhost instead and it will work:

spring.datasource.url=jdbc:postgresql://db:5432/robbankdb

See the documentation for explanation: https://docs.docker.com/network/drivers/bridge/#differences-between-user-defined-bridges-and-the-default-bridge