Tuning tman for golangci-lint
The lint that actually needs a memory ceiling — whole-program analysis is greedy.
Recommended starting caps
| cap | start at | what it answers |
|---|---|---|
stall | 10m | hang backstop — no output and no CPU/IO activity for this long |
max-time | 15m | wall-clock budget — the only cap that bounds a run that is working but will never finish |
max-mem | 6144 | RSS ceiling across the process tree |
max-parallel | 2 | slots per name-and-directory bucket |
These are starting points derived from how golangci-lint behaves, not measurements of your machine. Every one of them should be replaced by a number you took from your own runs — the last section on this page is how.
alias "lint" {
command "golangci-lint"
args "run" "--timeout" "10m"
max-time "15m"
max-mem 6144
}golangci-lint is the exception to the rule that linters are cheap. Several of its analysers need whole-program type information, which means loading and type-checking every package in the module at once. On a large repository that is gigabytes of resident memory and minutes of near-silence, which puts it closer to go test than to a linter.
This is where `max-mem` earns its keep
tman does not set a memory ceiling by default, because a build is supposed to be greedy and culling one out of the box would break ordinary work. golangci-lint is the case where opting in is clearly right: the failure mode without a ceiling is not a slow lint, it is the machine swapping and everything else on it becoming unusable. A 6 GB ceiling turns that into a clean exit 126.
When 126 does fire, the useful responses are to narrow the enabled analysers, to run against packages rather than ./..., or to raise the ceiling deliberately — in that order. Raising it first turns the ceiling into a rubber stamp.
Let the tool's own timeout fire first
golangci-lint --timeout exits with a message naming which linter was still running. tman's max-time kills the tree and reports 124. Set the tool's below tman's, exactly as with go test -timeout, so the diagnosis comes from the tool and the backstop only covers the case where the tool itself is wedged.
Turn the starting point into a real number
Everything above is derived from how this tool behaves, not from your machine. The numbers that matter come from your own runs, and getting them takes one command:
tman status --json | jq '.runs[] | {name, exit, duration_ms}'Set max-time from the slowest successful run you are willing to wait for, with real headroom — a cap that fires on healthy work teaches everyone to raise the cap, and after a few rounds of that it bounds nothing. Set stall from the longest stretch the command legitimately produces no output and no CPU, then double it.
Exit 124, 125, and 126 are findings, not flakes. 124 is a wall-clock timeout, 125 a stall, 126 a resource cull. Widening the cap that fired is the right response only after you know why it fired — the first two times a suite trips
max-mem, the interesting question is what is holding the memory.
Frequently asked
Why does golangci-lint need max-mem when other linters do not?
Because several of its analysers need whole-program type information, so it loads and type-checks every package in the module at once — gigabytes on a large repository. Without a ceiling the failure mode is the machine swapping rather than a slow lint. A 6 GB ceiling turns that into a clean exit 126.
Should I set --timeout as well as max-time?
Yes, and lower. golangci-lint's own timeout exits with a message naming the linter that was still running; tman's max-time kills the tree and reports 124. The tool's message is the more useful of the two, so let it fire first and keep tman's as the backstop for a wedged process.