Use an annotated tag for every release, and sign it if you can. Git's own manual says annotated tags are meant for release, while lightweight tags are meant for private or temporary labels. An annotated tag records who tagged, when, and why. A lightweight tag records none of that.
What each kind of tag is
A lightweight tag is a name that points at a commit. That is all. There is no object behind it, so there is nowhere to store a message or a signature.
An annotated tag is its own object in the repository. According to the git documentation, it holds a creation date, the tagger name and e-mail address, a tagging message, and an optional cryptographic signature.
| Property | Lightweight | Annotated | Annotated and signed |
|---|---|---|---|
| Tagger and date recorded | No | Yes | Yes |
| Message | No | Yes | Yes |
| Can carry a signature | No | No | Yes |
| Meant for releases (per git docs) | No | Yes | Yes |
Why it matters for a release
Three practical reasons.
- Accountability. When a user asks who cut version 2.4.0 and when, an annotated tag answers. A lightweight tag points at a commit and the commit author may be someone else entirely.
- Tooling behaviour. The manual notes that some git commands that name objects, such as
git describe, ignore lightweight tags by default. If your build embeds a version string fromgit describe, a lightweight release tag can be skipped. We cover that in a separate post on why git describe skips your tag. - A place for a signature. Only a tag object can carry one. A signed tag lets a downstream user check that the key holder vouched for that commit. See how to verify a signed git tag before you build.
How to create each
# lightweight
git tag v2.4.0
# annotated
git tag -a v2.4.0 -m "Release 2.4.0"
# annotated and signed with your configured key
git tag -s v2.4.0 -m "Release 2.4.0"
# push one tag
git push origin v2.4.0
Per the manual, passing a message with no explicit flag implies an annotated tag, but being explicit avoids surprises.
If you want signing to be the default, git has a configuration variable that signs annotated tags automatically when you create them. Check the current git-config page for the exact name and behaviour before you rely on it in scripts.
What a signature does and does not tell you
A signed tag shows that someone holding a particular key created it. It does not show the code is free of bugs or that the key holder was uncompromised. Treat it as one control among several: protected tags, a build in CI, and published checksums all cover different gaps.
A short release routine
- Make sure the release commit is on the protected default branch.
- Create a signed, annotated tag with a useful message.
- Push the tag and let CI build from it.
- Publish checksums or signatures for the built files.
Safeguard can scan the dependencies in the commit you are about to tag. The free scan is at gold.safeguard.sh/scan.
Quick check
Is a lightweight tag ever fine?
Yes, for private or temporary labels such as marking a commit while you debug. Do not use one as the public marker for a release.
Does signing a tag make it impossible to move?
No. A signature proves who created the tag object. Moving or deleting a tag is a repository permission question, which is why protecting release tags matters.
Sources
Last reviewed: 19 October 2026.