Skip to content
DevOps devops git 5 min read

Tags & Releases

A tag in Git is a permanent name you pin to one specific commit so you can find it again later. While branches keep moving as you add commits, a tag stays frozen on the exact commit it was created on. That makes tags perfect for marking releases — the precise snapshot of your code that you shipped to users. In DevOps this is huge, because your CI/CD pipeline (the automated system that builds and deploys your code) can watch for new tags and ship that exact version automatically.

Lightweight vs annotated tags

Git has two kinds of tags. Knowing the difference matters because one is barely more than a sticky note and the other is a real, traceable record.

A lightweight tag is just a name pointing at a commit. It stores nothing else — no author, no date, no message.

An annotated tag is a full object stored in the Git database. It records who made the tag, when, and a message, and it can be signed with a GPG key (a cryptographic signature that proves you really made it).

FeatureLightweight tagAnnotated tag
Stores author and dateNoYes
Has a messageNoYes
Can be GPG-signedNoYes
Good for releasesNoYes
Good for quick private bookmarksYesNot needed

When to use which: Always use annotated tags for anything that leaves your machine — releases, deploys, anything CI will react to. Use a lightweight tag only as a throwaway bookmark for yourself.

Gotcha: Lightweight tags are easy to create by accident (just git tag name with no flags). They have no metadata, so months later you cannot tell who made the release or why. Stick to -a for releases.

Semantic versioning

Before you tag, decide what to call the version. The industry standard is Semantic Versioning (SemVer). The version is three numbers: MAJOR.MINOR.PATCH, for example 1.4.2.

  • MAJOR — increase when you make a breaking change that forces users to change their code.
  • MINOR — increase when you add a new feature in a backward-compatible way.
  • PATCH — increase when you fix a bug without changing behavior.

Release tags almost always start with a lowercase v, like v1.0.0. The v is a long-standing convention that makes tags easy to spot and filter.

Change you madeOld versionNew version
Fixed a bugv1.4.2v1.4.3
Added a featurev1.4.2v1.5.0
Broke the APIv1.4.2v2.0.0

Creating and pushing a tag

Let’s tag the current commit as your first release, v1.0.0. The -a flag makes it annotated and -m supplies the message.

git tag -a v1.0.0 -m "First stable release"

This command prints nothing on success. To confirm the tag exists, list your tags:

git tag

Output:

v1.0.0

To see the full details of an annotated tag — the message, author, date, and the commit it points to — use git show:

git show v1.0.0

Output:

tag v1.0.0
Tagger: Alex Doe <[email protected]>
Date:   Mon Jun 15 10:22:31 2026 +0000

First stable release

commit 4f9a2c1e8b7d3a6f0c5e9b1d2a3f4c6e8d7b9a0c
Author: Alex Doe <[email protected]>
Date:   Mon Jun 15 10:20:04 2026 +0000

    Finalize release notes

Pushing tags to the remote

Here is the part that trips up almost everyone: git push does NOT push tags. A normal push only sends branch commits. You have to push the tag explicitly.

To push one specific tag:

git push origin v1.0.0

Output:

Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 178 bytes | 178.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To github.com:alex/myapp.git
 * [new tag]         v1.0.0 -> v1.0.0

To push every local tag at once:

git push origin --tags

Tagging an older commit

You don’t have to tag the latest commit. If you forgot to tag a release, find the commit hash with git log --oneline and pass it at the end:

git tag -a v0.9.0 4f9a2c1 -m "Beta release"
git push origin v0.9.0

Deleting a tag

If you tagged the wrong commit, delete it locally and on the remote:

git tag -d v1.0.0
git push origin --delete v1.0.0

Warning: Never re-use a tag name after others have pulled it. If someone already downloaded v1.0.0, moving the tag to a different commit means two people now have different code under the same version. Always bump to a new version instead.

How CI triggers on tags

This is where tags pay off. Most CI/CD platforms (the servers that build and deploy your code, like GitHub Actions or GitLab CI) can run a pipeline only when a tag is pushed. So your release process becomes: push a tag, walk away, and the build, test, and deploy happen automatically.

Here is a GitHub Actions workflow that runs only when you push a tag starting with v. Save it as .github/workflows/release.yml:

name: Release

on:
  push:
    tags:
      - "v*"

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the code
        uses: actions/checkout@v4

      - name: Build the project
        run: npm ci && npm run build

      - name: Create a GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          generate_release_notes: true

The tags: - "v*" part is the key. The pipeline ignores normal commits and pushes; it only wakes up for tags like v1.0.0 or v2.3.1. The softprops/action-gh-release step then turns that tag into a published GitHub Release with auto-generated notes.

On your Ubuntu server or CI runner you can also tag directly from a script. For example, a simple release helper:

#!/usr/bin/env bash
set -e
VERSION="$1"
git tag -a "v${VERSION}" -m "Release v${VERSION}"
git push origin "v${VERSION}"
echo "Tagged and pushed v${VERSION} — CI will take it from here."

Run it like ./release.sh 1.2.0, and your tag-triggered pipeline does the rest.

Best practices

  • Always use annotated tags (-a) for releases so authorship and a message are recorded.
  • Follow SemVer (MAJOR.MINOR.PATCH) and prefix release tags with v.
  • Remember tags are not pushed by git push — push them explicitly with git push origin <tag> or --tags.
  • Never move or re-use a published tag; bump to a new version instead.
  • Let CI trigger on tags so releases are automatic, repeatable, and hands-off.
  • Sign release tags with GPG (git tag -s) when you need to prove a release is genuinely yours.
  • Keep a CHANGELOG or use auto-generated release notes so each tag explains what changed.
Last updated June 15, 2026
Was this helpful?