GitLab’s release tracking is less about knowing when a version shipped and more about understanding why it shipped and what it actually contains.
Imagine you have a simple web app. You push code to a main branch. GitLab CI/CD kicks in.
stages:
- build
- deploy
build_app:
stage: build
script:
- echo "Building application..."
- echo "APP_VERSION=$(git rev-parse --short HEAD)" > build.env # Use short commit hash as version
- echo "Built successfully!"
artifacts:
reports:
dotenv: build.env
deploy_production:
stage: deploy
script:
- echo "Deploying version $APP_VERSION to production..."
- echo "Simulating deployment..."
- echo "Deployment complete for $APP_VERSION."
environment:
name: production
url: https://my-app.example.com
when: manual # Manual trigger for production
needs:
- job: build_app
artifacts: true
This pipeline builds the app, tags the artifact with the Git commit hash, and then has a manual job to deploy it to a production environment. The environment block is key here. It tells GitLab that this job is responsible for a specific deployment to a named environment, and crucially, it associates the deployment with the Git commit that was built.
When you click "play" on deploy_production, GitLab records this as a deployment. You can see this in the project’s Deployments section. This isn’t just a log; it’s a structured record. Each deployment entry shows:
- The environment (
production). - The commit SHA it was deployed from.
- The user who triggered it (or if it was automatic).
- The date and time.
- The status (success, failed, etc.).
This allows you to go to a specific commit in your Git history and see exactly which environments it was deployed to. Conversely, you can go to an environment (like production) and see the latest commit that was deployed there. This linkage is the core of GitLab’s release tracking.
What if you want to deploy to multiple environments sequentially? You can define them in your .gitlab-ci.yml:
stages:
- deploy
deploy_staging:
stage: deploy
script:
- echo "Deploying to staging..."
- echo "Simulating staging deployment."
environment:
name: staging
url: https://staging.my-app.example.com
deploy_production:
stage: deploy
script:
- echo "Deploying to production..."
- echo "Simulating production deployment."
environment:
name: production
url: https://my-app.example.com
when: manual
needs:
- deploy_staging # This ensures staging deploys first
Here, deploy_production depends on deploy_staging. If deploy_staging completes successfully, you can then manually trigger deploy_production. GitLab visualizes this flow in the Environments page, showing you the status of each environment and what’s currently deployed to it.
The real power comes when you combine this with GitLab Releases. You can create a formal "Release" in GitLab, which is essentially a tag in your Git repository. This tag can then be linked to a specific deployment.
release_app:
stage: release
script:
- echo "Creating Git tag for release..."
- git tag v1.0.0 HEAD # Tag the current commit
- git push origin v1.0.0 # Push the tag
- echo "Release v1.0.0 created and deployed to environments."
release:
tag_name: v1.0.0
description: "Release v1.0.0 - New feature X and bug fixes."
only:
- main # Only run on main branch
When this job runs, it creates a Git tag (v1.0.0 in this example) and simultaneously creates a GitLab Release associated with that tag. This Release can aggregate information about the commit, the associated deployments, and any release notes you provide. You can then navigate to Deployments > Releases and see your formally tagged releases, and for each release, see where it was deployed.
The most surprising thing about GitLab’s release tracking is how it leverages Git’s fundamental concepts (commits, tags) and extends them with structured metadata, turning a simple commit history into a verifiable audit trail of what code made it to which production system, and when. It’s not just about CI/CD pipelines; it’s about the traceability of every change.
If you have a job that uses the environment keyword, and it fails, GitLab will mark that environment as "stopped" or "unavailable" until a new job successfully deploys to it. This is because GitLab assumes a successful environment job means that environment is now running the code from that specific commit.
The next logical step is to integrate this with security scanning, so you can see the security posture of each deployed version.