Golangci-lint v2 was released in March 2025 , bringing major architectural improvements and a cleaner configuration format. Despite being available for over seven months, adoption remains low.
If you are maintaining a Go project, now is the time to upgrade. This article covers why you should make the switch and provides a step-by-step walkthrough using a real-world project.
I have contributed to Golangci-lint development since 2020 , reviewed pull requests for v2 changes, and authored the official migration guide.
Why migrate from v1 to v2
v1 is deprecated
The final v1 release is
v1.64
(Feb 12, 2025).
It supports Go 1.24 but will not support Go 1.25 or newer.
Staying on v1 means missing out on bug fixes, new linters, and future language support.
The latest version is
v2.6
.
Revamped configuration
The v2 configuration is more consistent and logical:
- Changed linter settings (see this proposal ).
- Updated exclusion rules (see this proposal ).
- Changed some default values.
- Removed obsolete flags.
These changes make v1 and v2 configurations incompatible, but migration is straightforward.
New fmt command
v2 introduces a dedicated fmt command. It acts like gofmt on steroids, running multiple formatters in a single pass:
gci, gofmt, gofumpt, goimports, golines, and swaggo.
Better documentation
The v2 website has been redesigned for easier navigation and better search functionality.
| v1 | v2 |
|---|---|
See this PR for details.
Screenshot of Golangci-lint v2 website (dark theme)

Migration caveats
To help users migrate, Golangci-lint includes the migration command and a
migration guide
.
Migration is generally easy thanks to the migration command, but there are two main caveats.
Comments are not preserved
The migration tool parses the YAML structure but cannot retain comments (see this PR for explanations). You will need to manually copy comments from your old v1 config to the new v2 file after running the command.
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
Deprecated options from v1 or unknown fields are removed
Deprecated linters and settings are removed entirely in v2.
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
Example: migrating the Lima project
To demonstrate a real-world migration, I upgraded Lima , a popular tool (19k+ stars) for launching Linux VMs.
The steps below use Lima as an example. While the process (install, migrate, fix) is universal, the specific linter exclusions shown here are specific to the Lima codebase.
Every migration of Golangci-lint to v2 consists of the following steps:
- Install Golangci-lint v2.
- Run
golangci-lint migrate. - Manually migrate comments from the v1 to v2 configuration file.
- Run Golangci-lint and deal with new lint issues.
- Upgrade the Golangci-lint version in CI.
The PR with the Golangci-lint migration in Lima, contributed by me, can be found here . Below is a step-by-step guide showing how I did it.
Let’s clone the project and switch to commit 0625d0b0 with the Golangci-lint v1
configuration
:
$ git clone https://github.com/lima-vm/lima.git
$ git switch 0625d0b0 -c chore/migrate-golangci-lint-v2
Switched to a new branch 'chore/migrate-golangci-lint-v2'
The Golangci-lint v1 configuration file in Lima has non-default linter configurations, many comments, deprecated linters, and several settings that changed in v2.
View .golangci.yml (v1) before migration
Install Golangci-lint v2
The installation manual on the official site is comprehensive and understandable :
$ brew install golangci-lint
$ golangci-lint version
golangci-lint has version 2.6.2 built with go1.25.4 from dc16cf4 on 2025-11-14T02:47:46Z
Run golangci-lint migrate
The following command automatically detects the
.golangci.yml
configuration and migrates to v2 in-place:
$ golangci-lint migrate
WARN The configuration comments are not migrated.
WARN Details about the migration: https://golangci-lint.run/docs/product/migration-guide/
WARN The configuration `run.timeout` is ignored. By default, in v2, the timeout is disabled.
╭───────────────────────────────────────────────────────────────────────────╮
│ │
│ We need you! │
│ │
│ Donations help fund the ongoing development and maintenance of this tool. │
│ If golangci-lint has been useful to you, please consider contributing. │
│ │
│ Donate now: https://donate.golangci.org │
│ │
╰───────────────────────────────────────────────────────────────────────────╯
View .golangci.yml (v2) after golangci-lint migrate
Migration changes
The main difference in the v2 configuration from v1 is version: "2" at the beginning:
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
The run.timeout setting is removed, which means no execution time limit by default:
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
The next change is the setting for disabling all default linters:
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
Updated linters in the enable setting.
The list is sorted alphabetically.
gofmt, gofumpt, and goimports are moved to formatters.
typecheck is
not a linter
and was removed.
gosimple and staticcheck are combined into staticcheck.
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
linters-settings are moved to linters.settings:
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
The issues.exclude-rules settings are moved to linters.exclusions.rules.
The issues.include settings are moved to linters.exclusions.presets.
Note that in v2, exclusions.paths are added that were always excluded by v1.
| v1 .golangci.yml | v2 .golangci.yml |
|---|---|
| |
Manually migrate comments from v1 to v2 configuration file
The old v1 configuration file is kept in .golangci.bck.yml, so we can compare changes and add comments to the v2 configuration manually:
$ git status -s
M .golangci.yml
?? .golangci.bck.yml
View .golangci.yml after copying comments from .golangci.bck.yml
Run Golangci-lint and deal with new lint issues
Run golangci-lint run:
$ golangci-lint run > golangci-lint-run-after-migrate.txt
$ tail -6 golangci-lint-run-after-migrate.txt
577 issues:
* noctx: 48
* nolintlint: 1
* perfsprint: 5
* revive: 449
* staticcheck: 74
View the full golangci-lint run log
A lot of issues, and you might feel confused, right? But it’s not so bad. Most of them can be easily excluded and fixed later.
First, enable
comments
exclusion preset to suppress comment-related issues:
exclusions:
generated: lax
presets:
- comments # <-- this line added
- common-false-positives
- legacy
- std-error-handling
This reduces the number of issues from 577 to 72:
$ golangci-lint run
...
72 issues:
* noctx: 48
* nolintlint: 1
* perfsprint: 5
* revive: 5
* staticcheck: 13
Next, apply these changes:
- Exclude
QFandST1001checks fromstaticcheck. - Exclude new
noctxissues fornet.Dial,net.Listen, andexec.Command. - Disable the
concat-loopcheck forperfsprint. - Allow using
UidandGidinpkg/osutil. - Rename
loggerWithoutTstologgerWithoutTSto satisfystaticcheck. - Disable
staticcheckforisColimaWrapper__useThisFunctionOnlyForPrintingHints__(generated code). - Remove the
nolintcomment to fix thenolintlintissue.
linters:
settings:
perfsprint:
int-conversion: false
err-error: false
errorf: true
sprintf1: false
strconcat: false
concat-loop: false # <-- this disables concat-loop
staticcheck:
checks:
- all
- "-SA3000"
- "-ST1001" # <-- this disables warn about using dot imports
- "-QF*" # <-- this disables QF checks
exclusions:
generated: lax
rules:
- linters:
- noctx
text: "os/exec.Command must not be called."
- linters:
- noctx
text: "net.* must not be called."
rules:
# Allow using Uid, Gid in pkg/osutil.
- path: pkg/osutil/
text: '(?i)(uid)|(gid)'
These changes eliminate the remaining issues:
$ golangci-lint run
0 issues.
Additionally, you can remove
generated
and the default exclusion paths because they are not used in Lima:
linters:
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
View the final migrated Golangci-lint configuration
Now you can remove .golangci.bck.yml, as it’s no longer needed.
Upgrade the Golangci-lint version in CI
Lima uses the following GitHub Actions workflow configuration to run Golangci-lint:
- name: Run golangci-lint
uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2
version: v1.64.2
args: --verbose --timeout=10m
All you need to do is update the golangci-lint-action and the version to the latest:
- name: Run golangci-lint
uses: golangci/golangci-lint-action@e7fa5ac41e1cf5b7d48e45e42232ce7ada589601 # v9.1.0
version: v2.6
args: --verbose
You can also remove the --timeout flag since this option is managed via the .golangci.yml configuration file.
Conclusion
Migrating to Golangci-lint v2 ensures your project remains compatible with modern Go versions and benefits from a stricter, more reliable configuration system.
See the PR as a migration example: https://github.com/lima-vm/lima/pull/3330 .
Support the Golangci-lint team: https://donate.golangci.org .

