Secure a GitLab project for OpenClaw
Use separate GitLab environments, protected branches, and environment-scoped
credentials to keep integration access out of production and production access
out of feature pipelines. This example uses the CI Tools GitFlow component with
develop for integration and main for production.
Delivery and access map
| Source | Target branch | GitLab environment | OpenClaw release | Credential scope | Who can deploy |
|---|---|---|---|---|---|
feat/*, fix/* | develop | none until merge | none | no deployment token | Developers through a reviewed merge request |
develop | develop | int | openclaw-int | GITLAB_TOKEN scoped to int | Developers and Maintainers |
hotfix/* | main | none until merge | none | no deployment token | Maintainers through a reviewed merge request |
main | main | prd | openclaw-prd | GITLAB_TOKEN scoped to prd | Maintainers only |
The branch controls which code is eligible for deployment. The job's
environment.name controls which token GitLab exposes. Both checks must match
before OpenClaw can receive an environment credential.
Configure the project workflow
Include the GitFlow component in .gitlab-ci.yml:
include:
- component: $CI_SERVER_FQDN/xrow-public/ci-tools/common@stable
- component: $CI_SERVER_FQDN/xrow-public/ci-tools/workflow-gitflow@stable
inputs:
development-branch: develop
The component creates and protects develop, protects main, requires
successful pipelines and resolved discussions, and configures project approval
behavior. Configure the merge request branch workflow in GitLab so feat/*
and fix/* target develop, while hotfix/* targets main.
Keep the reviewer allowlist in the project AGENTS.md, for example:
### Settings
| Setting | Value |
| --- | --- |
| reviewers | [platform-maintainer](https://gitlab.com/platform-maintainer) |
The GitLab agent requests one eligible reviewer from this list only after the branch is rebased, its pipeline succeeds, and the final diff and discussions are clean. Reviewers should have Maintainer or Owner access so they can merge into the protected target branches.
Create the environments and tokens
- Add deploy jobs whose
environment.namevalues are exactlyintandprd. Setdeployment_tiertostagingandproduction, respectively. - Protect
prdunder Settings > CI/CD > Protected environments and allow only Maintainers to deploy. Protectinttoo when integration deployments should be limited to a dedicated group. - Create a separate GitLab project access token for each OpenClaw release. Grant only the role and scopes the agent needs, set an expiry date, and plan rotation before expiry. Do not reuse a personal access token.
- Add both tokens under Settings > CI/CD > Variables with the same key,
GITLAB_TOKEN, but different environment scopes:intandprd. Set each variable to Masked and hidden and Protected. - Protect both
developandmain. A protected variable is not exposed to a pipeline running only from an unprotected feature branch.
Environment-scoped variables are selected from the deployment job's
environment.name, not from arbitrary rules. Do not use the secret variable
itself in rules or include conditions.
Deploy each OpenClaw release
Keep non-secret settings in version control and pass the environment token only to the matching deployment job:
.deploy-openclaw:
stage: deploy
image: registry.gitlab.com/xrow-public/ci-tools/tools:stable
script:
- umask 077
- secret_values="$(mktemp)"
- trap 'rm -f "${secret_values}"' EXIT
- >-
GITLAB_TOKEN_VALUE="${GITLAB_TOKEN}" yq -n
'.openclaw.secrets.GITLAB_TOKEN = strenv(GITLAB_TOKEN_VALUE)'
> "${secret_values}"
- >-
helm upgrade --install "openclaw-${CI_ENVIRONMENT_NAME}"
oci://registry.gitlab.com/xrow-public/helm-openclaw/charts/openclaw
--namespace "openclaw-${CI_ENVIRONMENT_NAME}"
--create-namespace
--values "${secret_values}"
deploy:int:
extends: .deploy-openclaw
environment:
name: int
deployment_tier: staging
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
deploy:prd:
extends: .deploy-openclaw
environment:
name: prd
deployment_tier: production
resource_group: openclaw-prd
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
The temporary values file is readable only by the job user and is deleted when
the job exits. GitLab masks the token in job output, but scripts must still
avoid set -x, printing secret values, rendering Secrets into artifacts, or
persisting the token in dotenv files. Prefer an external secrets manager when
the platform supports one.
Verify the boundary
Before enabling automatic production delivery, confirm:
- A
feat/*pipeline cannot read either deployment token. - The
developdeployment receives theinttoken but not theprdtoken. - The
maindeployment receives theprdtoken and requires an allowed production deployer. - The integration and production OpenClaw releases use different namespaces, tokens, and persistent volumes.
- The merge request has one reviewer selected from
AGENTS.md, a successful pipeline, and no unresolved discussions before merge. - Token expiry and rotation are monitored without storing token values in the repository, job artifacts, or OpenClaw workspace files.
See the GitLab documentation for environment-scoped CI/CD variables, protected environments, and access token security.