Redis often comes up in articles about caching, but many developers find themselves thinking “how do I actually use it in Spring Boot?” without ever taking the plunge.

This article walks through everything from spinning up Redis locally with Docker Compose to RedisTemplate basics, externalizing sessions with Spring Session, configuring Redis as the @Cacheable backend, and implementing Pub/Sub — with code examples for each use case. The target environment is Spring Boot 3.x / Java 17+. Redis operations, tuning, and Cluster configuration are not covered here.

Spinning Up Redis Locally with Docker Compose

First, let’s get Redis running in your local environment.

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

After running docker compose up -d, run docker compose exec redis redis-cli ping. If you get PONG back, you’re ready to go. If you place Redis in the same Compose file as your Spring Boot container, you can use the service name redis directly as the hostname. For more details on containerization, see How to Containerize a Spring Boot App with Docker.

Adding Dependencies and Configuring the Connection

Add spring-boot-starter-data-redis to your build.gradle.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

The connection configuration in application.yml is straightforward.

spring:
  data:
    redis:
      host: localhost  # Use the service name "redis" if inside Docker Compose
      port: 6379
      # password: your-password
      connect-timeout: 2s
      timeout: 1s

Enabling /actuator/health is handy because it lets you verify Redis connectivity at startup alongside other health checks.

Basic Operations with RedisTemplate

If you’re only dealing with strings, StringRedisTemplate is the easiest option.

@Service
@RequiredArgsConstructor
public class CacheService {

    private final StringRedisTemplate redisTemplate;

    public void save(String key, String value, long ttlSeconds) {
        redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttlSeconds));
    }

    public String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    public void updateExpire(String key, long ttlSeconds) {
        redisTemplate.expire(key, Duration.ofSeconds(ttlSeconds));
    }
}

If you also need to store POJOs, use RedisTemplate<String, Object> (configured in the next section).

Using GenericJackson2JsonRedisSerializer for Object Serialization

The default JdkSerializationRedisSerializer produces unreadable data in Redis and is fragile against class version changes. When storing POJOs, use GenericJackson2JsonRedisSerializer instead. It automatically adds a @class field to the stored JSON, so you don’t need to specify the type on deserialization. However, be aware that renaming a class or moving its package will break compatibility with existing stored data.

When passing a custom ObjectMapper, you must explicitly call activateDefaultTyping(), otherwise POJOs will be returned as LinkedHashMap. Adding this embeds type information in the JSON, allowing correct type restoration on retrieval. Note that if you’re only storing strings, Jackson2JsonRedisSerializer is sufficient.

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        var template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        var objectMapper = new ObjectMapper()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .setSerializationInclusion(JsonInclude.Include.NON_NULL)
            .activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL
            );
        var serializer = new GenericJackson2JsonRedisSerializer(objectMapper);

        template.setDefaultSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashValueSerializer(serializer);
        return template;
    }
}

Externalizing Sessions to Redis with Spring Session

When you scale out to multiple instances, in-memory sessions on each server are not shared. Spring Session + Redis solves this.

Assuming spring-boot-starter-data-redis is already added, include the following additional dependency:

implementation 'org.springframework.session:spring-session-data-redis'

In Spring Boot 3.x, session externalization is enabled simply by adding configuration to application.yml. This is the simplest approach.

spring:
  session:
    store-type: redis
    timeout: 30m

If you need finer control over timeout and configuration in code, you can use @EnableRedisHttpSession. However, in Spring Session 3.x, @EnableRedisIndexedHttpSession is the new recommended annotation. To avoid conflicts with application.yml auto-configuration, stick to one approach or the other.

With this in place, any instance that receives a request will correctly read the session. For stateless authentication with JWT, also check out Implementing Spring Security + JWT Authentication.

Using Redis as the Backend for @Cacheable

Define a RedisCacheManager to use Redis as the backend for @Cacheable.

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        var defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(10))
            .disableCachingNullValues();

        var perCacheConfig = Map.of(
            "products", defaultConfig.entryTtl(Duration.ofHours(1)),
            "rankings", defaultConfig.entryTtl(Duration.ofMinutes(1))
        );

        return RedisCacheManager.builder(factory)
            .cacheDefaults(defaultConfig)
            .withInitialCacheConfigurations(perCacheConfig)
            .build();
    }
}

The ability to set individual TTLs per cache name is particularly useful. For a detailed guide on using @Cacheable, see Implementing Caching with Spring Cache’s @Cacheable.

Implementing Message Sending and Receiving via Redis Pub/Sub

Redis Pub/Sub is useful for simple notifications and event delivery. Keep in mind that messages are not persisted — any messages sent while a subscriber is disconnected are lost.

@Slf4j
@Component
public class NotificationListener {
    public void handleMessage(String message) {
        log.info("Received: {}", message);
    }
}

@Configuration
public class PubSubConfig {

    @Bean
    public RedisMessageListenerContainer listenerContainer(
            RedisConnectionFactory factory, MessageListenerAdapter adapter) {
        var container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        container.addMessageListener(adapter, new PatternTopic("notification:*"));
        return container;
    }

    @Bean
    public MessageListenerAdapter messageListenerAdapter(NotificationListener listener) {
        return new MessageListenerAdapter(listener, "handleMessage");
    }
}

When using RedisTemplate<String, Object>, the serializer wraps messages with JSON-quoted type information, so use StringRedisTemplate’s convertAndSend() for publishing.

// Use StringRedisTemplate — RedisTemplate<String,Object> is NOT suitable here
stringRedisTemplate.convertAndSend("notification:user", "User logged in");

Using prefixes in channel names to distinguish purposes makes them easier to manage. For more advanced async processing needs, also refer to Async Processing in Spring Boot.

ReactiveRedisTemplate (For WebFlux Users)

If you’re using WebFlux, ReactiveRedisTemplate is available. The key difference from the imperative style is that opsForValue().set() returns a Mono<Boolean>.

ReactiveRedisTemplate is auto-configured even in Spring MVC applications, but the practical benefit is limited to WebFlux projects where you compose it into Reactor Mono/Flux chains. If the spring-boot-starter-webflux dependency is already present, ReactiveRedisConnectionFactory is auto-configured, so no additional Bean definition is needed. For standard Spring MVC applications, RedisTemplate is sufficient.

What to Check When You Get a Connection Error

If RedisConnectionFailureException appears at startup, check the following in order:

  1. Verify the hostname and port in application.yml are correct
  2. Confirm the Redis container is running (docker compose ps)
  3. Check that Spring Boot and Redis are on the same Docker network
  4. Check Redis status via /actuator/health

If you see a ClassCastException, stale data from before a serializer configuration change may be lingering. In development environments only, running redis-cli FLUSHDB to clear the entire DB often resolves the issue. In production, delete specific keys with DEL <key>.

Summary

This article introduced the main patterns for using Redis with Spring Boot, organized by use case.

  • RedisTemplate for CRUD operations on strings and objects
  • Spring Session to externalize sessions and support scale-out
  • RedisCacheManager to use Redis as the backend for @Cacheable
  • Pub/Sub to implement simple event notifications

The recommended starting point is StringRedisTemplate’s set/get. A natural progression is to add Spring Session when you need to scale out, then introduce RedisCacheManager when performance becomes a concern.