Why does Spring Boot try to connect to localhost:6379 when running inside Docker, even though I set SPRING_DATA_REDIS_HOST=app-redis in the environment variables? How can I make it connect to the Redis container correctly?
docker-compose.yml
services:
# Database (MySQL)
app-db:
image: mysql:8.0
container_name: myapp-db
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: myappdb
MYSQL_USER: myappuser
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
restart: unless-stopped
networks:
- myapp-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${DB_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Redis
app-redis:
image: redis:7-alpine
container_name: myapp-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
networks:
- myapp-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
# Spring Boot backend
app-backend:
build:
context: C:/aspring_workspace/cbt-enterprise
dockerfile: Dockerfile
container_name: myapp-backend
environment:
# License & Application Config
- LICENSE_KEY=${LICENSE_KEY}
- SPRING_APPLICATION_NAME=${SPRING_APPLICATION_NAME}
- SPRING_PROFILES_ACTIVE=docker
# Database Config
- SPRING_DATASOURCE_URL=jdbc:mysql://app-db:3306/${MYSQL_DATABASE}
- SPRING_DATASOURCE_USERNAME=${MYSQL_USER}
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD}
# Redis Config
- SPRING_DATA_REDIS_HOST=app-redis
- SPRING_DATA_REDIS_PORT=6379
- SPRING_DATA_REDIS_TIMEOUT=10000
# JWT Config
- JWT_SECRET=${JWT_SECRET}
- JWT_EXPIRATIONMS=${JWT_EXPIRATIONMS}
# Email Config
- SPRING_MAIL_USERNAME=${EMAIL_USERNAME}
- SPRING_MAIL_PASSWORD=${EMAIL_APP_PASSWORD}
- APP_MAIL_FROM=${EMAIL_USERNAME}
# Storage Config
- PROCTOR_STORAGE_TYPE=${STORAGE_TYPE}
- CBT_TEMPLATES_DIR=/templates
# AWS S3 Config
- AWS_S3_ACCESS_KEY=${AWS_ACCESS_KEY}
- AWS_S3_SECRET_KEY=${AWS_SECRET_KEY}
volumes:
- ./templates:/templates
depends_on:
app-db:
condition: service_healthy
app-redis:
condition: service_healthy
ports:
- "8080:8080"
restart: unless-stopped
networks:
- myapp-network
# Angular frontend
app-frontend:
build:
context: C:/angular_workspace
dockerfile: Dockerfile
container_name: myapp-frontend
depends_on:
- app-backend
ports:
- "4200:80"
restart: unless-stopped
networks:
- myapp-network
volumes:
db_data:
redis_data:
networks:
myapp-network:
driver: bridge
Exception (relevant part)
org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1858) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1789) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1586) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.lambda$getConnection$0(LettuceConnectionFactory.java:1566) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.doInLock(LettuceConnectionFactory.java:1527) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1563) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedConnection(LettuceConnectionFactory.java:1249) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:1055) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.core.RedisConnectionUtils.fetchConnection(RedisConnectionUtils.java:195) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:144) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:105) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:400) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:380) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.core.RedisTemplate.doWithKeys(RedisTemplate.java:802) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.core.RedisTemplate.keys(RedisTemplate.java:645) ~[spring-data-redis-3.5.1.jar:3.5.1]
at com.almubaraksuleiman.cbts.examiner.service.ExamTimerServiceImpl.updateTimers(ExamTimerServiceImpl.java:80) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) ~[spring-context-6.2.8.jar:6.2.8]
at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) ~[spring-context-6.2.8.jar:6.2.8]
at io.micrometer.observation.Observation.observe(Observation.java:498) ~[micrometer-observation-1.15.1.jar:1.15.1]
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[spring-context-6.2.8.jar:6.2.8]
at org.springframework.scheduling.config.Task$OutcomeTrackingRunnable.run(Task.java:85) ~[spring-context-6.2.8.jar:6.2.8]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-6.2.8.jar:6.2.8]
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[na:na]
at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to localhost/<unresolved>:6379
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:63) ~[lettuce-core-6.6.0.RELEASE.jar:6.6.0.RELEASE/643bd47]
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:41) ~[lettuce-core-6.6.0.RELEASE.jar:6.6.0.RELEASE/643bd47]
at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:354) ~[lettuce-core-6.6.0.RELEASE.jar:6.6.0.RELEASE/643bd47]
at io.lettuce.core.RedisClient.connect(RedisClient.java:220) ~[lettuce-core-6.6.0.RELEASE.jar:6.6.0.RELEASE/643bd47]
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.lambda$getConnection$1(StandaloneConnectionProvider.java:112) ~[spring-data-redis-3.5.1.jar:3.5.1]
at java.base/java.util.Optional.orElseGet(Optional.java:364) ~[na:na]
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.getConnection(StandaloneConnectionProvider.java:112) ~[spring-data-redis-3.5.1.jar:3.5.1]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1787) ~[spring-data-redis-3.5.1.jar:3.5.1]
... 30 common frames omitted
Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: localhost/127.0.0.1:6379
Caused by: java.net.ConnectException: Connection refused: no further information
at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na]
at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:946) ~[na:na]
at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:336) ~[netty-transport-4.1.122.Final.jar:4.1.122.Final]
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:339) ~[netty-transport-4.1.122.Final.jar:4.1.122.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:784) ~[netty-transport-4.1.122.Final.jar:4.1.122.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:732) ~[netty-transport-4.1.122.Final.jar:4.1.122.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:658) ~[netty-transport-4.1.122.Final.jar:4.1.122.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.122.Final.jar:4.1.122.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) ~[netty-common-4.1.122.Final.jar:4.1.122.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.122.Final.jar:4.1.122.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.122.Final.jar:4.1.122.Final]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
ExamTimerServiceImpl.java
package com.almubaraksuleiman.cbts.examiner.service;
import com.almubaraksuleiman.cbts.dto.ExamSessionService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
@RequiredArgsConstructor
public class ExamTimerServiceImpl {
private final StringRedisTemplate redisTemplate;
private final SimpMessagingTemplate messagingTemplate;
private ExamSessionService examSessionService;
private final TestAutoSubmitService autoSubmitService;
public void startTimer(Long sessionId, int durationInSeconds) {
String key = "exam:" + sessionId + ":timeLeft";
redisTemplate.opsForValue().set(key, String.valueOf(durationInSeconds));
messagingTemplate.convertAndSend("/topic/timer/" + sessionId, durationInSeconds);
}
public int getTimeLeft(Long sessionId) {
String key = "exam:" + sessionId + ":timeLeft";
String value = redisTemplate.opsForValue().get(key);
return value != null ? Integer.parseInt(value) : 0;
}
@Scheduled(fixedRate = 1000)
public void updateTimers() {
Set<String> keys = redisTemplate.keys("exam:*:timeLeft");
if (keys == null) return;
for (String key : keys) {
int time = Integer.parseInt(redisTemplate.opsForValue().get(key));
Long sessionId = Long.parseLong(key.split(":")[1]);
if (time > 0) {
redisTemplate.opsForValue().set(key, String.valueOf(time - 1));
messagingTemplate.convertAndSend("/topic/timer/" + sessionId, time - 1);
} else {
autoSubmitService.completeExam(sessionId);
redisTemplate.delete(key);
messagingTemplate.convertAndSend("/topic/timer/" + sessionId, "0");
messagingTemplate.convertAndSend("/topic/exam-completed/" + sessionId, "completed");
}
}
}
}
Dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Copy only the built jar
COPY target/*.jar app.jar
RUN addgroup -S spring && adduser -S spring -G spring
USER spring
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
application-docker.properties
# ===============================
# Redis (from docker-compose .env)
spring.data.redis.host=${SPRING_DATA_REDIS_HOST}
spring.data.redis.port=${SPRING_DATA_REDIS_PORT}
spring.data.redis.timeout=10000ms
#spring.redis.password=${SPRING_REDIS_PASSWORD}
application-docker.propertiesand there is nowhere in your execution of the application you are setting the docker profile.dockeris picked as profile, from mydocker-compose.ymlyou will see- SPRING_PROFILES_ACTIVE=docker. Every other services communicate with each other without any issue except for redis.