Picture a field device that has been running for eight months, misbehaving intermittently, and you have exactly one clue: a serial log with a kernel version string in it. Was that build the one with your fix, or the one before it? If your kernel builds don’t carry a distinguishing identifier, you cannot answer that question with confidence. This lecture in our free linux kernel development course covers CONFIG_LOCALVERSION — the small but essential mechanism for tagging exactly which build is running on exactly which device.
make kernelversion
make kernelrelease
uname
free linux development course
What You Will Learn
- The difference between the raw kernel version and the full kernel release string
- How to set
CONFIG_LOCALVERSIONand what it changes - Where the resulting identifier surfaces — in
uname, the module directory, and the boot log - Why editing
EXTRAVERSIONin the top-level Makefile is discouraged in favor of the Kconfig option - A practical tagging convention for build tracking across a product’s lifecycle
Prerequisites
- Comfort configuring the kernel with
menuconfig, covered in the previous lecture - A kernel source tree you can rebuild
Kernel Version Versus Kernel Release
The kernel actually exposes two related but distinct strings, and mixing them up causes confusion. Running the plain version target shows only the upstream version number baked into the source tree:
$ make kernelversion
6.9.0
That number never changes no matter how you configure the build — it reflects the version of the source code itself, not any customization you’ve applied. The release string is different: it starts from that same version but appends whatever local identifier you’ve configured, which is exactly where CONFIG_LOCALVERSION comes in.
Setting CONFIG_LOCALVERSION
CONFIG_LOCALVERSION lives in the General setup menu inside menuconfig, and it is a plain string-type Kconfig option — one of the five data types from our earlier lecture on Kconfig fundamentals. Say you’re building for a product codenamed “riverstone” and want to tag this as build 3:
$ make ARCH=arm menuconfig
# General setup ---> Local version - append to kernel release
# enter: -riverstone-b3
Equivalently, you can set it directly in .config:
CONFIG_LOCALVERSION="-riverstone-b3"
With that in place, the release target reflects the tag while the version target stays untouched:
$ make kernelversion
6.9.0
$ make kernelrelease
6.9.0-riverstone-b3
Where The Identifier Shows Up
Setting CONFIG_LOCALVERSION is not just cosmetic — the resulting release string propagates to every place the running system reports its kernel identity, which is exactly what makes it useful for field diagnostics.
| Where It Appears | How To See It |
|---|---|
| Boot log, first lines | dmesg | head or the serial console at boot |
| Running system identity | uname -r |
| Module install directory | /lib/modules/<kernelrelease>/ |
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Linux version 6.9.0-riverstone-b3 (build@ci-runner-04)
(gcc version 13.2.0) #1 SMP PREEMPT Tue Aug 12 10:04:02 UTC 2026
That single line, captured from a device’s serial log or a bug report, now tells you unambiguously which source tree, which product variant, and which build number produced the running kernel — no need to cross-reference build server logs or guess based on a timestamp.
Why Not Just Edit EXTRAVERSION In The Makefile?
The top-level kernel Makefile has its own EXTRAVERSION line, and editing it produces a similar effect to CONFIG_LOCALVERSION. It is technically possible, but discouraged, for a simple reason: it modifies a tracked source file that ships with every kernel release, which means your tag either has to be repeatedly re-applied after every upstream merge or it lives as a permanent diff against upstream that every rebase has to carry forward. CONFIG_LOCALVERSION, by contrast, lives in .config — a file that is already expected to be product-specific and version-controlled separately from the kernel source itself. Keep the tagging mechanism in configuration, not in source.
A Practical Tagging Convention
For teams shipping more than one hardware variant from a shared kernel tree, a structured tag pays for itself the first time you’re debugging a field return. A pattern that works well in practice:
CONFIG_LOCALVERSION="-<product>-<build-number>"
# examples:
CONFIG_LOCALVERSION="-riverstone-b3"
CONFIG_LOCALVERSION="-riverstone-lite-b12"
CONFIG_LOCALVERSION="-fieldtest-rc2"
Pair this with a CI pipeline that injects the build number automatically at build time, and every image your CI system produces carries a self-describing, greppable identity from the moment it boots.
Real-World Use Cases
- Field debugging — instantly know which build is on a returned device from its boot log alone
- Multi-variant products — distinguish a “lite” hardware SKU’s kernel from the flagship SKU’s kernel, even though both derive from the same source tree
- QA and release tracking — tag release-candidate builds distinctly (
-rc1,-rc2) so QA reports unambiguously reference a specific build
Common Mistakes And Troubleshooting
- Forgetting to set it at all — the release string then reads identically for two builds with meaningfully different configurations, making field triage guesswork
- Editing EXTRAVERSION directly — creates a permanent, easy-to-lose diff against the kernel Makefile instead of living in
.configwhere it belongs - Confusing kernelversion with kernelrelease — remember, only
kernelrelease(and everything downstream of it, likeuname -r) reflects your local tag - Reusing the same tag across builds with real behavioral differences — defeats the entire purpose; bump the build number every time the binary actually changes
Best Practices
- Automate the tag from your CI system so it’s never manually forgotten
- Keep the tag short but structured — product name plus build number is usually enough
- Never reuse a tag for two functionally different builds
- Document your tagging convention alongside your board support package so the whole team applies it consistently
Performance And Security Considerations
CONFIG_LOCALVERSION has no runtime performance cost — it is a string appended at build time. There is a minor security angle worth noting: the release string is visible to any unprivileged user via uname -r, so avoid embedding anything sensitive (internal hostnames, credentials, customer identifiers) in the tag. Stick to product codename and build number.
Summary And Key Takeaways
make kernelversionshows the fixed upstream version;make kernelreleaseshows that version plus your local tag- Set the tag through
CONFIG_LOCALVERSIONin the General setup menu, not by editing the Makefile’sEXTRAVERSION - The tag surfaces in the boot log,
uname -r, and the module install path — three places field diagnostics naturally look - A consistent product-plus-build-number convention turns every log snippet into a traceable build identifier
Conclusion
It costs almost nothing to set CONFIG_LOCALVERSION, and it can save hours the first time you need to identify exactly which kernel build is running on a device you can’t easily re-flash. Treat it as a standard part of your board support package configuration, not an afterthought. This lecture is part of EmbeddedPathashala’s free linux kernel development course; the next lecture shifts focus to loadable kernel modules and why many embedded products deliberately avoid them.
Frequently Asked Questions
What is the difference between make kernelversion and make kernelrelease?
kernelversion shows the fixed upstream version number; kernelrelease shows that version with your CONFIG_LOCALVERSION tag appended.
Where do I set CONFIG_LOCALVERSION?
In the General setup menu inside menuconfig, or directly as a line in .config.
Where does the LOCALVERSION tag actually show up on a running device?
In the boot log’s first lines, in the output of uname -r, and in the module install path under /lib/modules/.
Should I edit EXTRAVERSION in the Makefile instead?
It’s discouraged – it modifies a tracked source file and creates a diff that has to be reapplied on every merge, unlike the .config-based CONFIG_LOCALVERSION.
Is there a performance cost to setting LOCALVERSION?
None – it is purely a build-time string with no runtime effect.
Continue The Free Course
Next: why many embedded Linux products are built without loadable kernel modules at all.
