Tags pin moments; releases are tags with metadata
Branches move; tags do not. A tag is a name for one specific commit, and that pointer never changes once set. The use case is releases: tag v2.4.0 at the commit you shipped, and forever after, anyone can git checkout v2.4.0 and stand exactly where production stood at that release. The same is true for any moment worth recovering: a customer reproduces a bug "in v2.3.7" and you can put yourself there in seconds.
Two tag types matter. Lightweight tags (git tag v2.4.0) are just pointer files — a name for a commit, no metadata. Annotated tags (git tag -a v2.4.0 -m "Release 2.4.0") are full Git objects with author, date, message, and (optionally) GPG signature. For real releases, always use annotated tags. The signature confirms the release came from an authorized human; the message becomes part of the audit trail; git describe uses annotated tags to produce human-readable commit IDs like v2.4.0-12-g3f4a5d6.
git push --tags sends tags to the remote; many people are surprised by this — plain git push does NOT include tags. Configure git config --global push.followTags true to make tags follow normal pushes when they point at commits being pushed. git tag -d <name> deletes a local tag; git push origin --delete <name> deletes the remote tag. Be careful: tag deletion on a remote is rarely correct because anyone who already pulled the tag still has it.
GitHub's "Releases" feature is annotated tags plus a UI: release notes, downloadable assets (tarballs, binaries), pre-release flags. gh release create v2.4.0 --notes "..." from the CLI, or push a tag and use the web UI. Combined with semantic-release tooling and Conventional Commits, releases become an automatic side effect of merging — the team never writes release notes by hand again.