Setting up tag/release-based deployment in CI/CD

A release is built from a branch—and no one can say exactly what went to production. We set up tag- and release-label-based deployment end-to-end: pipeline, release policy, and clear rollback. Release time is reduced many times over, and release contents are visible at a glance.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Deploy by tag: where the release pipeline breaks

Typing main in the "branch" field and clicking "run" is a typical way to cut a release.

But a branch is not a version, but a moving reference: while the build is running, the next set of changes has already been merged into it, and the environment ends up with something other than what was in the code at the start.

Build reproducibility also breaks from within: dependencies are pulled from the network without pinning, and re-running from the same commit produces a different result.

The discrepancy is not noticed immediately. The release artifact is rebuilt for each environment, with its own set of variables, and production ends up with a build that exists neither in the repository nor in anyone's hands.

There is no way to find out exactly what went out: there is no tag, the commit hash is not recorded anywhere, and the release log is closed.

Rollback turns into rebuilding the previous state from memory, and while it is being prepared, the pipeline relies on manual production edits — they do not pass checks and disappear with the next release.

Deploy by tag removes this fork. A tag is an immutable label on a specific commit, and the artifact is built exactly once, tied to the tag, and reused across all environments: the same content passes the test environment and goes to production. We'll look at how to set up such a pipeline and where it still breaks.

What changes after moving to release tags

While deployment went from the head of the branch, the sign of "what is deployed" was the last commit: a slice that no one had checked in full could go to production.

A release tag removes this uncertainty — the run starts from an immutable pointer in Git, to which a specific slice of code and settings is attached.

The difference shows up not on the first day, but in metrics. Here is what we check in the first week after the transition to distinguish a real effect from formal order:

  • Share of successful releases — can you name, for any incident, the exact slice from which production was built, and does it match what passed checks?
  • Rollback time — is there a tag of the previous stable release that you return to with one action: minutes versus tens of minutes when building from a branch.
  • Number of incidents due to code/build mismatch — have cases disappeared where the artifact contained code that was not in the checked slice?
  • Release history — can it be read by tags: what went in, when, who confirmed.
  • Version control — are tags protected from overwriting, and is there a rule for who sets them and how.

If these items are covered, the release is reproducible by tag, rollback becomes switching to the previous one, and incident analysis starts with a specific slice, not with a search for "what was there."

Comparison: deploy by tag, by branch, or manually

The choice of release model determines what exactly goes to production and how repeatable the result is. Below are four options that are most common, with their boundaries of applicability and the cost they carry.

Option When it fits Limitations
Deploy by tag Product pipeline: the tag points to a specific code state; redeploying the same tag produces the same artifact Naming discipline is required — an extra tag creates a release. Fits poorly with frequent small changes
Deploy by branch Development and test environments where an automatic run is needed after each merge What goes to production is "what has accumulated": there is no explicit release boundary; rollback is possible only to the previous state of the branch
Manual release run Rare regulated releases, release within an agreed window Depends on a person: if the run is missed, production has an outdated build, and the deployment history is fragmented
Conditional release Release is triggered by an event: a tag, a change in a separate directory, closing a task Rules grow and conflict with each other; they are harder to explain to the team and verify before deployment

In practice, the approaches do not compete but divide the pipelines: a tag as the only entry to production, a branch as a quick response on environments, a manual run for exceptions such as an unscheduled fix.

Conditional release makes sense where the rule is simple and verifiable — for example, release only when a service directory changes. Next, we show how to bring this into a single pipeline without duplicating logic.

Release pipeline in CI/CD: stages of engineering work

The implementation stages are the same regardless of which build tool the customer uses: first audit and rules, then automation.

The goal is for the release to be triggered by a tag, not by a manually built package: then the version in production is unambiguously recoverable by identifier, and the dispute over "what is currently deployed" disappears.

  1. Pipeline audit. Inventory of branches, tags, and manual steps: where the artifact is built, how it gets to environments, which operations are done by hand. The output is a map of discrepancies between build and release.
  2. Versioning scheme. We fix the tag format (major.minor.patch and a separate prefix for pre-releases) and the rule for which tag leads to the test pipeline and which to the production one.
  3. Release rules. Protected tags, permissions to create them, mandatory checks before publication, manual confirmation for production.
  4. Setting up build from tag. One tag — one immutable artifact that is not rebuilt when promoted between environments.
  5. Setting up releases for deploy. Binding the tag to the environment, database migrations, health check after deployment, and automatic rollback to the previous tag on failure.
  6. Rehearsal. Run on one service: we measure the time from setting the tag to the working environment and separately the time to return to the previous version.
  7. Handover of control. Release regulations, description of rollbacks and access rights, training of the customer's on-call team.

What remains with the customer: configurations and regulations

Project handover is not an archive with source code, but a working system that the receiving team can run, change, and roll back without the author's involvement.

Therefore, by project closure a package is assembled in which each artifact has an owner and a clear usage scenario, not just the fact that a file exists.

  • Pipeline files — build and release descriptions are stored in the repository next to the code, not in web interface settings: changes go through review and are versioned together with the application.
  • Pipeline diagram — from build and checks to artifact publication and deployment, with explicitly marked manual confirmation points and places where the release stops automatically.
  • Environment matrix — which branches and tags go to the test and production pipelines, how their parameters differ, and how data and secrets are separated.
  • Access policies — roles for starting a release and confirming deployment to the production pipeline; for automation, separate service accounts are used instead of personal ones.
  • Release regulations — how the tag is formed, who has the right to set it, and what is considered the basis for a release, including the set of mandatory checks.
  • Rollback instructions — step-by-step return to the previous release: which commands, in what order, in what time the pipeline returns to a working state, and by what signs rollback is triggered.
  • Pipeline documentation — where secrets are stored and how they are updated, how dependencies are connected, which checks are blocking and which only warn.

The package is considered accepted not by signature, but in fact: the customer's engineer sets the tag themselves, takes the release to the production pipeline, and performs a training rollback.

We close remarks found at this step before handover — together with the diagrams and regulations, they remain inside the project and do not require contacting us for explanations.

Case study: releases by tags instead of building from a branch

The customer's product team managed several services and built releases directly from the main branch.

The release had no immutable point: the environment state could not be described by a single identifier, so "return as it was" meant rebuilding — and a new build from the same branch might not match what was actually running.

Before the implementation case, we measured three values over a quarter: a release took from 35 to 50 minutes of manual operations, every fourth release ended in a rollback, and in about every fifth case the artifact in the environment did not match the branch code — new commits had managed to arrive in it.

What was changed

The move to tags made the tag the only source of release. The build pipeline builds the artifact only from a tagged commit in Git and places it in immutable storage, and deployment to the production environment from a branch is prohibited by rules.

Rollback is not a rebuild but a switch to the previous tag, with the same command as the release. The tag registry became a release log: from one identifier you can see what went out and when.

Project results

Metric Before After
Release time 35–50 minutes 6–8 minutes
Release failures every fourth release one in twenty
Build/code discrepancies about 20% of releases not reproducible
Return to previous release about an hour a few minutes

Six months later, the team began releasing more often, not less: a release stopped being a risky event, and release metrics are now read from tag history rather than manually collected from logs.

What to do if the release is already built from a branch?

Yes, moving to tags does not require rewriting the pipeline. A tag and a branch are both pointers to a commit, so the build, test, and artifact publication steps remain the same. Two things change: the trigger condition and where the version number comes from.

The main work is to move the version out of the template into a parameter. Currently the number is usually calculated from the branch name or set manually, and this is exactly what prevents running by tag.

Once the version comes from outside, from the tag itself, the pipeline becomes indifferent to the trigger source, and the switch comes down to configuring the trigger.

It is worth implementing this gradually, without breaking the working scheme. First, running by tag is added in parallel: the build from the branch continues to release intermediate versions, and the tag triggers the release pass through the same steps.

We run the first releases in both directions and compare the artifacts — the composition and checksum must match, otherwise a dependency on the branch is hardcoded in some step.

The history is not broken by this. Tags can be applied retroactively to already released commits — old releases will get the same designations as new ones, and the "tag — artifact — environment" link will become continuous.

After that, stable releases go out only by tag, and the branch remains a development tool; you can return to the previous scheme at any stage while the parallel run is not disabled.

The final step is a rule prohibiting publication to production without a tag and prohibiting rebuilding an existing one: one tag produces one immutable artifact. If an error is found in a release, a new tag is issued, not an overwrite of the old one. This is what makes rollback predictable: the environment returns to a specific fixed artifact.

Frequently asked questions about release tags and rollbacks

Doubts about release tags usually come down to four topics: who has the right to release, how to roll back, what about parallel versions, and whether a working scheme will have to be broken. Below is how this works in the pipeline.

Who has the right to set a release tag?

The right to release is held by a limited set of roles: the tag is set by the person responsible, and direct deployment from the working branch is closed by pipeline settings. This rule cannot be bypassed — checks are performed regardless of who is releasing.

How to roll back if a release turned out to be bad?

The artifact is built once and stored immutably together with the tag, so rolling back a release is deploying the previous tag, not rebuilding. Returning to a working version takes minutes and does not depend on what has been changed in the branch.

Can several versions be maintained in parallel?

Yes: each support line has its own branch and its own tags, so a fix in an old version does not pull in changes from a new one. Parallel versions live in different environments and do not interfere with each other.

What will happen to an already working release scheme?

Pipeline improvements proceed in stages: the old method remains working until the new one is tested on a production release. Switching is done one service at a time, with the ability to revert.

How long are release artifacts stored?

The retention policy is set explicitly: releases by each tag remain available for rollback, the rest is removed on a schedule. Storage does not grow indefinitely.

Let's discuss your release scheme and come back with a plan

For the deploy-by-tag scheme to work in your infrastructure and not remain a set of rules in a separate repository, a plan is needed for your current release process: how versions are assigned now, who sets the release tag and at what point, how many environments the artifact passes through, and what is considered a rollback.

Describe this in free form — branching, the build method, and where manual steps are now are enough. Our engineers will analyze the scheme and come back with a work estimate and implementation plan: what we do at the start of the project, what later, and what can be left as is.

  • Implementation plan by stages: release tag marking and rules for their appearance, building the artifact from a fixed state, promotion across environments
  • Rollback scheme: return to the previous tag without rebuilding, preserving environment configuration and data
  • Access and signature policies: who has the right to release a tag, and how artifact authenticity is confirmed
  • Separation of build and deployment: what is reused between environments, and where differences in settings remain
  • Set of checks before deployment: which automatic gates let a release through, and which require confirmation
  • Documentation and handover: description of the release scheme, instructions for the on-call person, analysis of typical failures

Describe the task — we will come back with a work estimate and a plan that fixes the stages, areas of responsibility, and launch order.