{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreifqpryb6p6h6uewnh2yqp2khw3mjblwlf7fwkw62gsng6eezu3qh4",
"uri": "at://did:plc:wszrgoqdwy3i2dfeub2mt3wf/app.bsky.feed.post/3mkvbvd6ew7i2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreibk7ilcb5quri2t525amggpz4athfmo466xujf7qb2bl5tiggd75m"
},
"mimeType": "image/jpeg",
"size": 26144
},
"description": "How to make sure that Renovate's updates to Go modules keep you within the minor version of your `go` directive.",
"path": "/posts/2026/05/02/renovate-only-go-directive/",
"publishedAt": "2026-05-02T18:24:27.000Z",
"site": "https://www.jvt.me",
"tags": [
"blogumentation",
"renovate",
"go",
"go directive",
"toolchain directive",
"via go tool",
"oapi-codegen",
"we aim to sparingly update the go directive",
"working upstream officially",
"Renovate 43.159.2",
"constraintsFiltering=strict"
],
"textContent": "In the Go ecosystem, there are two key parts of the `go.mod` to note (aside from the dependencies themselves) which are the `go` and `toolchain` directives.\n\nThe go directive allows specifying the _minimum version of the Go language_ that needs to be used to work with this module.\n\nThe toolchain directive allows specifying the minimum version of the Go toolchain that needs to be run to build the module, which also controls the version of Go's standard library that is used with the module (and therefore which security patches the standard library has applied).\n\nIn Go 1.21, the Go team introduced `toolchain`, and moved the `go` directive from being a \"suggested\" minimum version to a mandatory version. Splitting the two of these allow you to use a newer version of Go (for instance on your local machine, or as part of your Docker image) while still being clear on what the compatibility of the code should be and allowing for code from a much older version of Go to be used, and it allows for two levels of control over how a given module is used.\n\nWhen building applications (such as command-line tools that users install separately, or when building web services), it's common to keep the `go` directive as high as the author would like, so you can take advantage of new features of Go, and the `toolchain` as high as possible, to make sure you're receiving security patches.\n\nWhen building a library (or tool that is a dependency in the project, for instance via go tool), the inverse is true - it is common to be quite reserved with the `go` directive, and not set the `toolchain` directive at all.\n\nThis comes out of a \"ripple effect\" that can occur when a library updates its `go` directive, requiring each of its consumers to also update their own `go` directive. Because Go requires the `go` directive to be a minimum version, if a dependency was to require a new minor version of Go, it means each and every of its dependencies also need to do so, at the point they upgrade to the new version.\n\nFor instance, with oapi-codegen, we recommend the use of `go tool` as the means to track us as a tool dependency, which means that we appear in the `go.mod` of our user's projects, and each time we require a new Go version, that also means _all of our users_ must do the same. To avoid this, we aim to sparingly update the go directive, which reduces the fancy new Go language features we can use, but does give us the consistency for our users.\n\nFor the most part, we're using versions of Go that are now officially unsupported, but in some organisations, it requires a fair bit of effort to (technically or organisationally) update the Go versions across the org, so being quite reserved with our `go` directive is something we've had much positive feedback about.\n\nLong before I started working upstream officially, I had been using Renovate with `oapi-codegen` to update our dependencies, and it was doing the job well. But as new dependency updates started upgrading their `go` directive, this started leading to a lot of PRs from Renovate that we wouldn't be able to merge.\n\nWe had a few, slightly hacky, alternatives we could apply, but I felt there would be a better way, and about a month ago I started looking into what options we would have for this.\n\nAs of last week Renovate 43.159.2 is now able to only raise updates to dependencies that stay within the `go` directive's minor version.\n\nThis allows you to write Renovate config such as:\n\n\n {\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"packageRules\": [\n {\n \"description\": \"Ensure that dependency updates do not bump the `go` directive, and only suggests updates that stay within the `go` directive (allowing a patch bump at most)\",\n \"matchManagers\": [\n \"gomod\"\n ],\n \"constraintsFiltering\": \"strict\"\n }\n ]\n }\n\n\nWith this, we take advantage of constraintsFiltering=strict to perform filtering on the Datasource (in this case, the Go module proxy, or a custom proxy) and look up the `go.mod` for the new version, skipping the update if it doesn't match the current minor version.\n\nNote that - as the docs suggest - constraintsFiltering=strict **is strict**. It is worth considering if there are any trade-offs you're unwilling to make, such as no longer receiving security update PRs, if they were to bump the `go` directive.\n\nThis is something that I'm very much enjoying already, and it's taken us from ~40 pending PRs to ~10, and now gives us only the actionable PRs!",
"title": "Configuring Renovate to only suggest updates that match your go directive.",
"updatedAt": "2026-05-02T18:24:27.000Z"
}