{
  "schema_version": 2,
  "id": "develop/clients/go/produsage",
  "title": "Production usage",
  "url": "https://redis.io/docs/latest/develop/clients/go/produsage/",
  "summary": "Get your `go-redis` app ready for production",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-10-12T14:54:04+01:00",
  "page_type": "content",
  "content_hash": "233109dda9f86337bdd4ea3dd4679544233fd9702be8c5c0f28a5f13024cbfd9",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "This guide offers recommendations to get the best reliability and\nperformance in your production environment."
    },
    {
      "id": "checklist",
      "title": "Checklist",
      "role": "content",
      "text": "Each item in the checklist below links to the section\nfor a recommendation. Use the checklist icons to record your\nprogress in implementing the recommendations.\n\n[code example]"
    },
    {
      "id": "recommendations",
      "title": "Recommendations",
      "role": "content",
      "text": "The sections below offer recommendations for your production environment. Some\nof them may not apply to your particular use case."
    },
    {
      "id": "health-checks",
      "title": "Health checks",
      "role": "content",
      "text": "If your code doesn't access the Redis server continuously then it\nmight be useful to make a \"health check\" periodically (perhaps once\nevery few seconds). You can do this using a simple\n[`PING`](https://redis.io/docs/latest/commands/ping) command:\n\n[code example]\n\nHealth checks help to detect problems as soon as possible without\nwaiting for a user to report them."
    },
    {
      "id": "error-handling",
      "title": "Error handling",
      "role": "errors",
      "text": "The `Result()` method of a command returns both the command result\nand an error value. Although you are mainly interested in the result,\nyou should also always check that the error value is `nil` before\nproceeding. Errors can be returned for failed connections, network\nproblems, and invalid command parameters, among other things.\n\nSee [Error handling](https://redis.io/docs/latest/develop/clients/go/error-handling) for a\nmore detailed discussion of error handling approaches in `go-redis`."
    },
    {
      "id": "monitor-performance-and-errors",
      "title": "Monitor performance and errors",
      "role": "content",
      "text": "`go-redis` supports [OpenTelemetry](https://opentelemetry.io/). This lets\nyou trace command execution and monitor your server's performance.\nYou can use this information to detect problems before they are reported\nby users. See [Observability](https://redis.io/docs/latest/develop/clients/go/observability)\nfor more information.\n\nTo find the keys that use the most CPU time and network bandwidth on the\nserver, use the [`HOTKEYS`](https://redis.io/docs/latest/commands/hotkeys) command."
    },
    {
      "id": "retries",
      "title": "Retries",
      "role": "content",
      "text": "`go-redis` will automatically retry failed connections and commands. By\ndefault, the number of attempts is set to three, but you can change this\nusing the `MaxRetries` field of `Options` when you connect. The retry\nstrategy starts with a short delay between the first and second attempts,\nand increases the delay with each attempt. The initial delay is set\nwith the `MinRetryBackoff` option (defaulting to 8 milliseconds) and the\nmaximum delay is set with the `MaxRetryBackoff` option (defaulting to\n512 milliseconds):\n\n[code example]\n\nYou can use the observability features of `go-redis` to monitor the\nnumber of retries and the time taken for each attempt, as noted in the\n[Monitor performance and errors](#monitor-performance-and-errors) section\nabove. Use this data to help you decide on the best retry settings\nfor your application."
    },
    {
      "id": "timeouts",
      "title": "Timeouts",
      "role": "content",
      "text": "`go-redis` supports timeouts for connections and commands to avoid\nstalling your app if the server does not respond within a reasonable time.\nThe `DialTimeout` field of `Options` sets the timeout for connections,\nand the `ReadTimeout` and `WriteTimeout` fields set the timeouts for\nreading and writing data, respectively. The default timeout is five seconds\nfor connections and three seconds for reading and writing data, but you can\nset your own timeouts when you connect:\n\n[code example]\n\nYou can use the observability features of `go-redis` to monitor the\nfrequency of timeouts, as noted in the\n[Monitor performance and errors](#monitor-performance-and-errors) section\nabove. Use this data to help you decide on the best timeout settings\nfor your application. If timeouts are set too short, then `go-redis`\nmight retry commands that would have succeeded if given more time. However,\nif they are too long, your app might hang unnecessarily while waiting for a\nresponse that will never arrive."
    },
    {
      "id": "connection-pooling",
      "title": "Connection pooling",
      "role": "content",
      "text": "`go-redis` manages connections for you with a\n[connection pool](https://redis.io/docs/latest/develop/clients/pools-and-muxing), so your\napp doesn't have to cache and reuse open connections itself. The `PoolSize`\nfield of `Options` sets the number of\nconnections the main pool keeps (with a default of ten times the value of `GOMAXPROCS`). `MinIdleConns` sets how many\nconnections to open before your app asks for them, `MaxActiveConns` caps the\ntotal number of connections, and `PoolTimeout` sets how long a command waits\nfor a free connection. By default, no connections are opened in advance, the\ntotal is uncapped, and a command waits one second longer than `ReadTimeout`.\n\nBy default, pipelines don't use the main pool. In go-redis v9.23.0 or later,\nevery client also creates a separate *pipeline pool* that\n[pipelines and transactions](https://redis.io/docs/latest/develop/clients/go/transpipe) and\n[automatic pipelining](https://redis.io/docs/latest/develop/clients/go/autopipeline) use\nfor each batch they run. This prevents a burst of batches from competing with your\nordinary commands for the same connections.\n\nUse the following fields of `Options` to size the pipeline pool:\n\n| Field | Description |\n| :---- | :---------- |\n| `PipelinePoolSize` | Number of connections the pipeline pool keeps. Defaults to 10. Set it to `0` to use the default number of connections, or to `-1` to disable the separate pipeline pool (which means connections are allocated to pipelines from the main pool). |\n| `PipelineReadBufferSize` | Size of the read buffer for each pipeline connection. Defaults to the larger of `ReadBufferSize` and 128 KiB, because a batch of commands can return many replies in a single round trip. If the value is set smaller than the minimum required by the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) protocol for push messages then that minimum is used instead. |\n| `PipelineWriteBufferSize` | Size of the write buffer for each pipeline connection. Defaults to the larger of `WriteBufferSize` and 128 KiB. |\n\nThe pipeline pool costs you nothing while you are not using pipelining. It never opens\nconnections in advance regardless of the value of `MinIdleConns`, so an idle pipeline\npool holds no connections at all. Also, it doesn't use\n`MaxActiveConns` as its upper limit (which would double the number of connections your client can\nopen). The limit is instead the value of `MaxActiveConns` plus `PipelinePoolSize`.\n`ClusterClient` and `Ring` create a pipeline pool for each node, so this limit\napplies for each node rather than for each client.\n\nIf every pipeline connection is busy, a batch runs on the main pool straight away\nrather than waiting for a pipeline connection. A burst of batches wider than\n`PipelinePoolSize` therefore opens extra connections in the main pool, which\nstill applies the `MaxActiveConns` limit. A `Limiter` counts such a batch once\nrather than twice.\n\nYou can find out the current size of the pipeline pool using the `PipelineStats`\nfield from the client's pool statistics:\n\n[code example]\n\nNo field counts the batches that run on the main pool, and they don't appear\nas timeouts. If `PipelineStats.TotalConns` stays at `PipelinePoolSize` while the\nmain pool's `TotalConns` grows during bursts of batches, increase\n`PipelinePoolSize`. Note that `PipelineStats` is `nil` when you disable the\npipeline pool (by setting `PipelinePoolSize` to `-1`). It also combines\nthe figures from every node for `ClusterClient` and `Ring` into the same count."
    },
    {
      "id": "smart-client-handoffs",
      "title": "Smart client handoffs",
      "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.\n\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH and\n[Connect using Smart client handoffs](https://redis.io/docs/latest/develop/clients/go/connect#connect-using-smart-client-handoffs-sch)\nfor example code."
    }
  ],
  "examples": [
    {
      "id": "checklist-ex0",
      "language": "checklist {id=\"goprodlist\"}",
      "code": "- [ ] [Health checks](#health-checks)\n- [ ] [Error handling](#error-handling)\n- [ ] [Monitor performance and errors](#monitor-performance-and-errors)\n- [ ] [Retries](#retries)\n- [ ] [Timeouts](#timeouts)\n- [ ] [Connection pooling](#connection-pooling)\n- [ ] [Smart client handoffs](#smart-client-handoffs)",
      "section_id": "checklist"
    },
    {
      "id": "health-checks-ex0",
      "language": "go",
      "code": "err := rdb.Ping(ctx).Err()\n\nif err != nil {\n  // Report failed health check.\n}",
      "section_id": "health-checks"
    },
    {
      "id": "retries-ex0",
      "language": "go",
      "code": "client := redis.NewClient(&redis.Options{\n    MinRetryBackoff: 10 * time.Millisecond,\n    MaxRetryBackoff: 100 * time.Millisecond,\n    MaxRetries: 5,\n})",
      "section_id": "retries"
    },
    {
      "id": "timeouts-ex0",
      "language": "go",
      "code": "client := redis.NewClient(&redis.Options{\n    DialTimeout:  10 * time.Second,\n    ReadTimeout:  5 * time.Second,\n    WriteTimeout: 5 * time.Second,\n})",
      "section_id": "timeouts"
    },
    {
      "id": "connection-pooling-ex0",
      "language": "go",
      "code": "stats := client.PoolStats()\nfmt.Printf(\"Main pool connections: %d, hits: %d, misses: %d\\n\",\n    stats.TotalConns, stats.Hits, stats.Misses)\nif ps := stats.PipelineStats; ps != nil {\n    fmt.Printf(\"Pipeline pool connections: %d, hits: %d, misses: %d\\n\",\n        ps.TotalConns, ps.Hits, ps.Misses)\n}",
      "section_id": "connection-pooling"
    }
  ]
}
