{
  "schema_version": 2,
  "id": "develop/clients/lettuce/connect",
  "title": "Connect to the server",
  "url": "https://redis.io/docs/latest/develop/clients/lettuce/connect/",
  "summary": "Connect your Java application to a Redis database",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-10-12T14:48:04+01:00",
  "page_type": "content",
  "content_hash": "b6a9ea80aa89efda80465512c5246358419a6ce5cb61cdf231e2151fcdb6f008",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "Start by creating a connection to your Redis server. There are many ways to achieve this using Lettuce. Here are a few."
    },
    {
      "id": "basic-connection",
      "title": "Basic connection",
      "role": "content",
      "text": "[code example]\n\n> [!NOTE]\n> Lettuce supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#lettuce-java) through `RedisClient.create()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately."
    },
    {
      "id": "connect-to-a-redis-cluster",
      "title": "Connect to a Redis cluster",
      "role": "content",
      "text": "To connect to a Redis cluster, use `RedisClusterClient`. \n\n[code example]\n\nLearn more about Cluster connections and how to configure them in [the reference guide](https://redis.github.io/lettuce/ha-sharding/#redis-cluster)."
    },
    {
      "id": "connect-to-redis-sentinel",
      "title": "Connect to Redis Sentinel",
      "role": "content",
      "text": "To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\nbuild a `RedisURI` with `RedisURI.Builder.sentinel()`, passing the address of one Sentinel\nand the name of the primary that the Sentinels monitor. Add the other Sentinels with\n`withSentinel()`. The client asks the Sentinels for the current primary.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Pass the Sentinel password with\neach Sentinel address, and set the credentials for the primary and replicas with\n`withAuthentication()`:\n\n[code example]\n\nA `RedisClient` connection doesn't listen for failover announcements from the Sentinels.\nAfter a failover, it keeps using the old primary until Sentinel reconfigures it as a\nreplica and closes the connection. Any writes that the old primary accepts during that time\nare lost.\n\nTo read from replicas, open a connection with `MasterReplica.connect()` and set a\n`ReadFrom` policy. This connection also listens for failover announcements from the\nSentinels, so it switches to a new primary sooner. Replication is asynchronous, so a\nreplica can return stale data.\n\n[code example]"
    },
    {
      "id": "asynchronous-connection",
      "title": "Asynchronous connection",
      "role": "content",
      "text": "[code example]\n\nLearn more about asynchronous Lettuce API in [the reference guide](https://redis.github.io/lettuce/#asynchronous-api)."
    },
    {
      "id": "reactive-connection",
      "title": "Reactive connection",
      "role": "content",
      "text": "[code example]\n\nLearn more about reactive Lettuce API in [the reference guide](https://redis.github.io/lettuce/#reactive-api)."
    },
    {
      "id": "connect-to-a-redis-cluster-1",
      "title": "Connect to a Redis cluster",
      "role": "content",
      "text": "[code example]"
    },
    {
      "id": "tls-connection",
      "title": "TLS connection",
      "role": "content",
      "text": "When you deploy your application, use TLS and follow the [Redis security guidelines](https://redis.io/docs/latest/operate/oss_and_stack/management/security).\n\n[code example]"
    },
    {
      "id": "connection-management-in-lettuce",
      "title": "Connection Management in Lettuce",
      "role": "content",
      "text": "Lettuce uses `ClientResources` for efficient management of shared resources like event loop groups and thread pools.\nFor connection pooling, Lettuce leverages `RedisClient` or `RedisClusterClient`, which can handle multiple concurrent connections efficiently."
    },
    {
      "id": "connection-pooling",
      "title": "Connection pooling",
      "role": "content",
      "text": "A typical approach with Lettuce is to create a single `RedisClient` instance and reuse it to establish connections to your Redis server(s).\nThese connections are multiplexed; that is, multiple commands can be run concurrently over a single or a small set of connections, making explicit pooling less practical.\nSee\n[Connection pools and multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nfor more information.\n\nLettuce provides pool config to be used with Lettuce asynchronous connection methods.\n\n[code example]\n\nIn this setup, `LettuceConnectionFactory` is a custom class you would need to implement, adhering to Apache Commons Pool's `PooledObjectFactory` interface, to manage lifecycle events of pooled `StatefulRedisConnection` objects."
    },
    {
      "id": "connect-using-smart-client-handoffs-sch",
      "title": "Connect using Smart client handoffs (SCH)",
      "role": "content",
      "text": "*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH.\n\n> [!NOTE]\n> Support for SCH in Lettuce requires v7.0.0 or later.\n\nBy default, `Lettuce` always attempts to connect via SCH but falls back to\na non-SCH connection if the server doesn't support it. However, you can configure SCH\nexplicitly by creating a `MaintNotificationsConfig` object and/or a `TimeoutOptions`\nobject and passing them to the `ClientOptions` builder as shown in the example below.\nNote that SCH also requires the\n[RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\nprotocol. Lettuce uses this by default, but make sure you don't set\n`protocolVersion(ProtocolVersion.RESP2)` in the `ClientOptions` builder.\n\n[code example]\n\nTo disable SCH, use `MaintNotificationsConfig.disabled()` and pass the instance it\nreturns to the `ClientOptions` builder:\n\n[code example]\n\nThe `MaintNotificationsConfig` builder accepts the following options:\n\n| Method | Description |\n|--------|-------------|\n| `enableMaintNotifications(boolean enabled)` | Enable/disable SCH. The default is `true` (enabled). |\n| `endpointType(EndpointType type)` | Set the type of endpoint to use for the connection. The options are `EndpointType.EXTERNAL_IP`, `EndpointType.INTERNAL_IP`, `EndpointType.EXTERNAL_FQDN`, `EndpointType.INTERNAL_FQDN`, and `EndpointType.NONE`. Use the separate `autoResolveEndpointType()` method to auto-detect based on the connection (this is the default behavior). |\n| `autoResolveEndpointType()` | Auto-detect the type of endpoint to use for the connection. This is the default behavior. Use `endpointType()` to set a specific endpoint type. |\n\nAmong other options, the `TimeoutOptions` builder accepts the following option\nthat is relevant to SCH:\n\n| Method | Description |\n|--------|-------------|\n| `relaxedTimeoutsDuringMaintenance(Duration duration)` | Set the *command* timeout to use while the server is performing maintenance (this doesn't change the connection timeout). The default is 10 seconds. Note that relaxed timeouts are only available for the asynchronous and reactive APIs. |\n|\n\n> [!NOTE]\n> Redis Cloud supports relaxed timeouts *only* (and not pre-handoffs) for SCH if you are using\n> either [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) or\n> [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)\n> (see [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch#redis-cloud) for more information).\n> To use relaxed timeouts with these services, you should set `endpointType(EndpointType.NONE)`\n> when you connect. All other configurations have full support for both relaxed timeouts and pre-handoffs."
    }
  ],
  "examples": [
    {
      "id": "basic-connection-ex0",
      "language": "java",
      "code": "import io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.sync.RedisCommands;\n\npublic class ConnectBasicTest {\n\n    public void connectBasic() {\n        RedisURI uri = RedisURI.Builder\n                .redis(\"localhost\", 6379)\n                .withAuthentication(\"default\", \"yourPassword\")\n                .build();\n        RedisClient client = RedisClient.create(uri);\n        StatefulRedisConnection<String, String> connection = client.connect();\n        RedisCommands<String, String> commands = connection.sync();\n\n        commands.set(\"foo\", \"bar\");\n        String result = commands.get(\"foo\");\n        System.out.println(result); // >>> bar\n\n        connection.close();\n\n        client.shutdown();\n    }\n}",
      "section_id": "basic-connection"
    },
    {
      "id": "connect-to-a-redis-cluster-ex0",
      "language": "java",
      "code": "import io.lettuce.core.cluster.RedisClusterClient;\nimport io.lettuce.core.cluster.api.StatefulRedisClusterConnection;\n\n//...\ntry (RedisClusterClient clusterClient = RedisClusterClient.create(redisURI)) {    \n    StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();\n        \n    //...\n    \n    connection.close();    \n}",
      "section_id": "connect-to-a-redis-cluster"
    },
    {
      "id": "connect-to-redis-sentinel-ex0",
      "language": "java",
      "code": "import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.sync.RedisCommands;\n\n//...\n\nRedisURI redisUri = RedisURI.Builder\n        .sentinel(\"localhost\", 26379, \"mymaster\")\n        .withSentinel(\"localhost\", 26380)\n        .withSentinel(\"localhost\", 26381)\n        .build();\n\ntry (RedisClient client = RedisClient.create(redisUri)) {\n    StatefulRedisConnection<String, String> connection = client.connect();\n    RedisCommands<String, String> commands = connection.sync();\n\n    commands.set(\"foo\", \"bar\");\n    System.out.println(commands.get(\"foo\")); // >>> bar\n\n    connection.close();\n}",
      "section_id": "connect-to-redis-sentinel"
    },
    {
      "id": "connect-to-redis-sentinel-ex1",
      "language": "java",
      "code": "RedisURI redisUri = RedisURI.Builder\n        // The password argument authenticates to each Sentinel.\n        .sentinel(\"localhost\", 26379, \"mymaster\", \"yourSentinelPassword\")\n        .withSentinel(\"localhost\", 26380, \"yourSentinelPassword\")\n        .withSentinel(\"localhost\", 26381, \"yourSentinelPassword\")\n        // withAuthentication() authenticates to the primary and replicas.\n        .withAuthentication(\"default\", \"yourPassword\")\n        .build();",
      "section_id": "connect-to-redis-sentinel"
    },
    {
      "id": "connect-to-redis-sentinel-ex2",
      "language": "java",
      "code": "import io.lettuce.core.ReadFrom;\nimport io.lettuce.core.codec.StringCodec;\nimport io.lettuce.core.masterreplica.MasterReplica;\nimport io.lettuce.core.masterreplica.StatefulRedisMasterReplicaConnection;\n\n//...\n\ntry (RedisClient client = RedisClient.create()) {\n    StatefulRedisMasterReplicaConnection<String, String> connection =\n            MasterReplica.connect(client, StringCodec.UTF8, redisUri);\n    connection.setReadFrom(ReadFrom.REPLICA);\n\n    System.out.println(connection.sync().get(\"foo\")); // >>> bar\n\n    connection.close();\n}",
      "section_id": "connect-to-redis-sentinel"
    },
    {
      "id": "asynchronous-connection-ex0",
      "language": "java",
      "code": "package org.example;\nimport java.util.*;\nimport java.util.concurrent.ExecutionException;\n\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\npublic class Async {\n  public static void main(String[] args) {\n    RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n    try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n      RedisAsyncCommands<String, String> asyncCommands = connection.async();\n\n      // Asynchronously store & retrieve a simple string\n      asyncCommands.set(\"foo\", \"bar\").get();\n      System.out.println(asyncCommands.get(\"foo\").get()); // prints bar\n\n      // Asynchronously store key-value pairs in a hash directly\n      Map<String, String> hash = new HashMap<>();\n      hash.put(\"name\", \"John\");\n      hash.put(\"surname\", \"Smith\");\n      hash.put(\"company\", \"Redis\");\n      hash.put(\"age\", \"29\");\n      asyncCommands.hset(\"user-session:123\", hash).get();\n\n      System.out.println(asyncCommands.hgetall(\"user-session:123\").get());\n      // Prints: {name=John, surname=Smith, company=Redis, age=29}\n    } catch (ExecutionException | InterruptedException e) {\n      throw new RuntimeException(e);\n    } finally {\n      redisClient.shutdown();\n    }\n  }\n}",
      "section_id": "asynchronous-connection"
    },
    {
      "id": "reactive-connection-ex0",
      "language": "java",
      "code": "package org.example;\nimport java.util.*;\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\npublic class Main {\n  public static void main(String[] args) {\n    RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n    try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n      RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n\n      // Reactively store & retrieve a simple string\n      reactiveCommands.set(\"foo\", \"bar\").block();\n      reactiveCommands.get(\"foo\").doOnNext(System.out::println).block(); // prints bar\n\n      // Reactively store key-value pairs in a hash directly\n      Map<String, String> hash = new HashMap<>();\n      hash.put(\"name\", \"John\");\n      hash.put(\"surname\", \"Smith\");\n      hash.put(\"company\", \"Redis\");\n      hash.put(\"age\", \"29\");\n\n      reactiveCommands.hset(\"user-session:124\", hash).then(\n              reactiveCommands.hgetall(\"user-session:124\")\n                  .collectMap(KeyValue::getKey, KeyValue::getValue).doOnNext(System.out::println))\n          .block();\n      // Prints: {surname=Smith, name=John, company=Redis, age=29}\n\n    } finally {\n      redisClient.shutdown();\n    }\n  }\n}",
      "section_id": "reactive-connection"
    },
    {
      "id": "connect-to-a-redis-cluster-1-ex0",
      "language": "java",
      "code": "import io.lettuce.core.RedisURI;\nimport io.lettuce.core.cluster.RedisClusterClient;\nimport io.lettuce.core.cluster.api.StatefulRedisClusterConnection;\nimport io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;\n\n// ...\n\nRedisURI redisUri = RedisURI.Builder.redis(\"localhost\").withPassword(\"authentication\").build();\n\nRedisClusterClient clusterClient = RedisClusterClient.create(redisUri);\nStatefulRedisClusterConnection<String, String> connection = clusterClient.connect();\nRedisAdvancedClusterAsyncCommands<String, String> commands = connection.async();\n\n// ...\n\nconnection.close();\nclusterClient.shutdown();",
      "section_id": "connect-to-a-redis-cluster-1"
    },
    {
      "id": "tls-connection-ex0",
      "language": "java",
      "code": "RedisURI redisUri = RedisURI.Builder.redis(\"localhost\")\n                                 .withSsl(true)\n                                 .withPassword(\"secret!\") // use your Redis password\n                                 .build();\n\nRedisClient client = RedisClient.create(redisUri);",
      "section_id": "tls-connection"
    },
    {
      "id": "connection-pooling-ex0",
      "language": "java",
      "code": "package org.example;\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.TransactionResult;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.codec.StringCodec;\nimport io.lettuce.core.support.*;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CompletionStage;\n\npublic class Pool {\n  public static void main(String[] args) {\n    RedisClient client = RedisClient.create();\n\n    String host = \"localhost\";\n    int port = 6379;\n\n    CompletionStage<BoundedAsyncPool<StatefulRedisConnection<String, String>>> poolFuture\n        = AsyncConnectionPoolSupport.createBoundedObjectPoolAsync(\n            () -> client.connectAsync(StringCodec.UTF8, RedisURI.create(host, port)),\n            BoundedPoolConfig.create());\n\n    // await poolFuture initialization to avoid NoSuchElementException: Pool exhausted when starting your application\n    AsyncPool<StatefulRedisConnection<String, String>> pool = poolFuture.toCompletableFuture()\n        .join();\n\n    // execute work\n    CompletableFuture<TransactionResult> transactionResult = pool.acquire()\n        .thenCompose(connection -> {\n\n          RedisAsyncCommands<String, String> async = connection.async();\n\n          async.multi();\n          async.set(\"key\", \"value\");\n          async.set(\"key2\", \"value2\");\n          System.out.println(\"Executed commands in pipeline\");\n          return async.exec().whenComplete((s, throwable) -> pool.release(connection));\n        });\n    transactionResult.join();\n\n    // terminating\n    pool.closeAsync();\n\n    // after pool completion\n    client.shutdownAsync();\n  }\n}",
      "section_id": "connection-pooling"
    },
    {
      "id": "connect-using-smart-client-handoffs-sch-ex0",
      "language": "java",
      "code": "import io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.protocol.ProtocolVersion;\nimport java.time.Duration;\n//  ...\n//  ...\n\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\nMaintNotificationsConfig maintNotificationsConfig = MaintNotificationsConfig.builder()\n        .enableMaintNotifications()\n        // .autoResolveEndpointType() // default is auto-resolve\n        .endpointType(EndpointType.INTERNAL_IP)\n        .build();\n\nTimeoutOptions timeoutOptions = TimeoutOptions.builder()\n        .relaxedTimeoutsDuringMaintenance(Duration.ofSeconds(10))\n        .build();\n\nClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(maintNotificationsConfig)\n        .timeoutOptions(timeoutOptions)\n        // SCH requires RESP3. Don't override the default protocol version\n        // to RESP2:\n        // .protocolVersion(ProtocolVersion.RESP2) // <- Wrong\n        .build();\n\nredisClient.setOptions(clientOptions);\n\n// or\nClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(MaintNotificationsConfig.enabled(EndpointType.INTERNAL_IP))\n        .build();\n\nredisClient.setOptions(clientOptions);\n\n// or\nClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(MaintNotificationsConfig.enabled())\n        .build();\n\nredisClient.setOptions(clientOptions);",
      "section_id": "connect-using-smart-client-handoffs-sch"
    },
    {
      "id": "connect-using-smart-client-handoffs-sch-ex1",
      "language": "java",
      "code": "ClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(MaintNotificationsConfig.disabled())\n        .build();\n\nredisClient.setOptions(clientOptions);",
      "section_id": "connect-using-smart-client-handoffs-sch"
    }
  ]
}
