2

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}
2
  • 1
    how many profiles you have in your application because you show the application-docker.properties and there is nowhere in your execution of the application you are setting the docker profile. Commented Oct 2 at 20:21
  • Yes, I do have multiple profiles, application-docker.properties and application-local.properties, when running local , springboot application output logs showing local is picked as the profile,same as when running in docker springboot application output logs showing docker is picked as profile, from my docker-compose.yml you will see - SPRING_PROFILES_ACTIVE=docker. Every other services communicate with each other without any issue except for redis. Commented Oct 3 at 0:16

1 Answer 1

1

change redis host app-redis -> myapp-redis don't use servicename use container_name

Sign up to request clarification or add additional context in comments.

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.