and Approvals for Staging and Production
A deployment to staging can happen automatically, a deployment to production needs a deliberate approval. GitLab Environments, when: manual and deployment approvals implement exactly this approval model, complete with a visible deployment history and variable scoping based environment isolation.
Table of Contents
- 1. What GitLab Environments are and why they matter for Magento
- 2. Configuring environments in .gitlab-ci.yml
- 3. Staging: automatic deployment after merge
- 4. Production: manual deployment with explicit approval
- 5. Environment approvals: formal sign-off in GitLab Premium
- 6. Deployment history: visibility across all environments
- 7. Variable scoping by environment
- 8. Comparison: deployment approval models
- 9. Common pitfalls with environments and approvals
- 10. Summary
- 11. FAQ
1. What GitLab Environments are and why they matter for Magento
GitLab Environments are named target environments for deployments, declared in the pipeline configuration. When a job contains environment: name: staging, GitLab links the job to the staging environment and logs every deployment in the environment's history. The result is a clear overview in the GitLab interface that shows which version is currently deployed to which environment, who triggered the deploy and when it happened.
For Magento projects, environments are especially valuable because the path from a feature to production passes through several stages: development (local), staging (shared test environment), pre production (optional, production like testing) and production. GitLab Environments make this path visible, complete with a URL to the currently deployed version, linked directly from the environment overview.
Another benefit of declared environments is the integration with variable scoping: variables can be configured for a specific environment and are only available in jobs that reference that environment. This makes it technically impossible for a staging deploy job to receive production credentials, even if the variable names are identical. That is a structural security measure that is not possible without a declared environment scope.
2. Configuring environments in .gitlab-ci.yml
The basic configuration of an environment in .gitlab-ci.yml consists of a name and, optionally, a URL. The name must be consistent with the GitLab environment configuration and the variable scope. Typical environment names for Magento projects are staging and production. If a client has multiple shops, environments can also be organized with a namespace, for example staging/shop-de and production/shop-de.
The url property links the environment to the actual URL of the shop. GitLab shows this URL directly in the pipeline overview as a clickable link, which speeds up verification after a deployment. The value can be static or built from a variable, which is useful for dynamic environments such as review apps.
# environments.yml - Environment definitions for a Magento project
# Staging: auto-deploy on merge to main
deploy:staging:
stage: deploy
environment:
name: staging
url: "https://staging.shop.example.com"
on_stop: teardown:staging # Optional: cleanup job
script:
- ./bin/deploy.sh staging
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: on_success # Automatic after successful test stage
# Production: manual trigger only, from tagged releases
deploy:production:
stage: deploy
environment:
name: production
url: "https://shop.example.com"
script:
- ./bin/deploy.sh production
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
when: manual # Requires human confirmation in GitLab UI
allow_failure: false # Pipeline stays pending until triggered
3. Staging: automatic deployment after merge
In most teams, staging deployments should trigger automatically after a successful merge into the main branch. This assumes that the preceding stages, build, test, quality checks, have succeeded. Automatic staging deployment gives the team a continuously current version to test without anyone having to intervene manually. At the same time, staging is the foundation that ensures production approvals are based on an already tested state.
An important configuration for automatic staging deployments is the needs directive, which ensures that the deploy job only starts once all dependent build and test jobs have completed successfully. Without explicit dependencies, a staging deployment could in theory start in parallel with tests that are still running, depending on the pipeline configuration. With needs, the order is guaranteed.
For Magento projects, an automatic staging deployment typically means: transferring artifacts to the staging server, setting symlinks, linking shared files, running setup:upgrade (if database migrations exist), deploying static content, flushing the cache and running a smoke test against the staging URL. The whole process typically takes 3 to 7 minutes and gives the team immediate feedback after every merge.
4. Production: manual deployment with explicit approval
Production deployments require a deliberate decision. In GitLab this is implemented with when: manual in the deployment job. The job appears in the pipeline but is paused, waiting for a manual trigger by an authorized user. allow_failure: false ensures the pipeline status shows as "waiting" rather than "failed" or "success", which makes the urgency of the pending approval step visible.
The combination of manual production jobs and protected branches ensures that only authorized people can trigger production deployments. If the production job only runs on a protected tag (for example v1.2.3), only maintainers and owners of the project can trigger the job. Regular developers see the button but cannot click it. This access control is a technical enforcement of the four-eyes principle.
# Production deployment with manual gate and proper scoping
.deploy:base:
script:
- eval $(ssh-agent -s)
- echo "${SSH_PRIVATE_KEY}" | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- echo "${SSH_KNOWN_HOSTS}" >> ~/.ssh/known_hosts
- rsync -az --delete \
--exclude='.git' \
--exclude='var/' \
--exclude='pub/media/' \
"./" "${DEPLOY_USER}@${DEPLOY_HOST}:${RELEASE_PATH}/"
- ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "bash -s" < ./bin/post-deploy.sh
deploy:staging:
extends: .deploy:base
stage: deploy
environment:
name: staging
url: "https://staging.shop.example.com"
variables:
RELEASE_PATH: "${STAGING_DEPLOY_PATH}/releases/$(date +%Y%m%d-%H%M%S)"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: on_success
deploy:production:
extends: .deploy:base
stage: deploy
environment:
name: production
url: "https://shop.example.com"
variables:
RELEASE_PATH: "${PROD_DEPLOY_PATH}/releases/${CI_COMMIT_TAG}"
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
when: manual # Explicit human approval required
allow_failure: false
5. Environment approvals: formal sign-off in GitLab Premium
GitLab Premium and Ultimate offer environment approvals as a formal approval process. When an environment is configured with an approval process, specific people or groups must explicitly give their consent before a deployment job in that environment is allowed to run. This differs from a simple manual job because the approval appears as its own event in the audit logs and is documented in a traceable way.
Environment approvals are configured in the GitLab project settings under CI/CD > Environments. There you can set, for each environment, how many approvals are required and which users or groups are configured as approvers. This setting is deliberately kept outside .gitlab-ci.yml so a developer cannot bypass it with a commit to the pipeline file.
For teams without GitLab Premium there is a pragmatic alternative: a separate manual job called approve:production placed before the actual deploy job, serving as an approval step. This job has no real function other than the confirmation click, but it appears in the pipeline history and makes visible who approved it and when. Combined with GitLab's user audit log, this produces a complete approval record.
6. Deployment history: visibility across all environments
The deployment history in GitLab is one of the most valuable, yet least known, features. Under Operate > Environments, GitLab shows a chronological list of all deployments for each environment, with a timestamp, the triggering job, commit SHA, branch or tag name and the GitLab user who triggered the deploy. From there you can jump directly into a rollback job for a specific historical deployment.
For Magento teams, the deployment history is a practical answer to the question "what is currently running on production?". Instead of needing SSH access to the server or reading log files, the GitLab overview immediately gives you the information: tag v1.2.3, deployed on 09.05.2026 at 14:33, by user admin@mironsoft.de. This is useful not only for day to day operations but also for audit evidence and incident analysis.
# Rollback job tied to the environment for history integration
rollback:production:
stage: rollback
environment:
name: production
action: rollback # Marks this as a rollback in the deployment history
when: manual
script:
- ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "bash -s" << 'ENDSSH'
set -euo pipefail
# List last 5 releases for operator reference
echo "Available releases for rollback:"
ls -lt "${PROD_DEPLOY_PATH}/releases/" | head -6 | tail -5
# ROLLBACK_TO variable must be set in manual trigger
TARGET="${PROD_DEPLOY_PATH}/releases/${ROLLBACK_TO:?Variable ROLLBACK_TO required}"
test -d "${TARGET}" || { echo "ERROR: Release not found"; exit 1; }
ln -sfn "${TARGET}" "${PROD_DEPLOY_PATH}/current"
cd "${PROD_DEPLOY_PATH}/current"
bin/magento cache:flush
echo "Rolled back to ${TARGET}"
ENDSSH
variables:
ROLLBACK_TO: "" # Must be provided via manual trigger variables
rules:
- when: manual
allow_failure: false
7. Variable scoping by environment
Variable scoping is the technical foundation that lets staging and production deployments use different servers, paths and credentials without hardcoding this configuration into the pipeline file. In the GitLab project settings, each variable can be given an environment scope: staging or production. A job that declares environment: name: staging only receives the staging variables; the production job only receives the production variables.
Scoping also works with wildcards: a scope of review/* applies to every environment whose name starts with review/. This is especially useful for review apps that create their own temporary environment for every feature branch. Variables with the scope * apply to all environments and act as the fallback when no more specific scope matches.
A common question is whether environment scoping also applies to secrets such as SSH keys. The answer is yes: if SSH_PRIVATE_KEY is configured for staging and for production as separate variables with different scopes, the staging job uses the staging SSH key and the production job uses the production SSH key. It is technically not possible for a staging job to extract the production SSH key if the scoping is configured correctly.
8. Comparison: deployment approval models
When deciding how to implement production approvals, it helps to compare the available models. Each model has different prerequisites and offers a different degree of formalization and auditability.
| Model | Requirement | Auditability | Suited for |
|---|---|---|---|
| when: manual | GitLab Free | Basic (user plus timestamp) | Small teams, simple processes |
| Environment Approvals | GitLab Premium | Complete (dedicated audit event) | Compliance, PCI DSS, ISO 27001 |
| Protected Tags + Manual | GitLab Free | Tag creation plus deploy event | Release based workflows |
| Approve Job + Deploy Job | GitLab Free | Two separate audit events | PCI proxy without a Premium license |
| Fully automatic (CD) | Mature pipeline plus tests | Complete pipeline log | Mature CD processes, SaaS |
For Magento e-commerce projects with regulatory requirements, the recommended configuration is the combination of protected tags, a manual deploy job and, where available, environment approvals. It offers clear approval semantics, is auditable and makes it visible to everyone involved which version is running on production and who approved it.
9. Common pitfalls with environments and approvals
A frequent pitfall is a misconfigured variable scope, where staging variables affect production jobs or the other way around. This happens when variables are configured without a scope and therefore apply to all environments. In that case, staging and production are not isolated, a security risk and a source of configuration errors. The fix is consistent scoping: every environment specific variable gets an explicit environment scope.
A second pitfall involves when: manual jobs without allow_failure: false. If the manual deployment job is never triggered, GitLab marks it as "skipped" by default and the whole pipeline as "passed". That means the pipeline shows green even though no production deployment took place. Anyone who does not explicitly check the pipeline details may miss the pending deploy. With allow_failure: false, the pipeline stays in the "waiting for manual action" status, which communicates the urgency far better.
A third pitfall is a rollback job that is not properly linked to the environment. If the rollback job does not declare an environment with action: rollback, it will not appear in the environment's deployment history. That makes it harder to reconstruct, after a rollback, what state existed and when. Every job that changes the state of an environment, deploy, rollback or teardown, should reference the environment correctly.
10. Summary
GitLab Environments, Manual Jobs and approvals for staging and production form the foundation for a traceable, controlled deployment process. Environments make deployments visible and link variable scoping to concrete target environments. Automatic staging deployments give the team continuous feedback. Manual production jobs enforce a deliberate approval by authorized personnel. Environment approvals in GitLab Premium formalize this process for compliance requirements.
The correct interplay of these mechanisms, environment declaration, variable scoping, protected tags and manual approvals, is the foundation for Magento deployments that not only work technically but are also organizationally controlled and auditable. Anyone who implements this configuration from the start builds a deployment process that is efficient during normal operation and gives clear answers about the current and historical system state whenever an incident occurs.
GitLab Environments and Approvals: the essentials at a glance
Staging automatic
when: on_success after merge to main. needs dependencies for the correct order. Fully automatic once tests pass.
Production manual
when: manual with allow_failure: false. Only on protected tags. Only maintainers can trigger it. Clear approval timestamp in the audit log.
Variable scoping
Configure every environment specific variable with an environment scope. Staging and production are technically isolated.
Deployment history
Operate > Environments shows a chronological deploy history. Link rollback jobs with action: rollback. Always traceable.