# Deploy from GitLab CI

Prerequisites: a running Workload, its service instance id, the [deploy token](/guides/ci/overview#create-a-token-that-can-only-deploy), and [`zaroz-deploy.sh`](/guides/ci/shell) committed as `deploy/zaroz-deploy.sh`.

## Variables

In **Settings > CI/CD > Variables**:

| Key | Settings |
|---|---|
| `ZAROZ_TOKEN` | the `zaroz_...` token. **Masked** and **Protected**, so only pipelines on protected branches (`main`) can read it |
| `ZAROZ_SERVICE` | the service instance UUID |
| `CI_DEPLOY_USER`, `CI_DEPLOY_PASSWORD` | only for a private image. Create a **deploy token** with only `read_registry` (Settings > Repository > Deploy tokens) and GitLab fills these two variables for jobs automatically when its name is `gitlab+deploy-token` |

## The pipeline

```yaml
# .gitlab-ci.yml
stages: [build, deploy]

variables:
  IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA   # one immutable tag per commit

build:
  stage: build
  image: docker:27
  services: [docker:27-dind]
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker build -t "$IMAGE" .
    - docker push "$IMAGE"

deploy:
  stage: deploy
  image: alpine:3.20
  needs: [build]
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  # Never two deployments at once, and never an older commit over a newer one.
  resource_group: production
  environment: production
  before_script:
    - apk add --no-cache curl jq
  script:
    - sh deploy/zaroz-deploy.sh
  variables:
    # ZAROZ_TOKEN and ZAROZ_SERVICE: Settings > CI/CD > Variables
    # (ZAROZ_TOKEN masked and protected). A private registry also needs these two,
    # for a deploy token with read_registry:
    REGISTRY_USERNAME: $CI_DEPLOY_USER
    REGISTRY_PASSWORD: $CI_DEPLOY_PASSWORD
```


How it behaves:

- `resource_group: production` makes GitLab run **one** deploy job at a time and keep them in order, so an older commit never lands on top of a newer one.
- `needs: [build]` starts the deploy the moment the image is pushed.
- The deploy job fails when the rollout fails, after putting the previous image back. The `environment: production` entry gives you a deployment history in GitLab beside Zaroz's.
- Add `when: manual` to the deploy job's `rules` for a push-button production deploy.

## Roll back

In **Deploy > Environments > production**, press the rollback button on an older deployment: GitLab re-runs that commit's deploy job, which deploys that commit's image. By hand: `IMAGE=$CI_REGISTRY_IMAGE:<old sha> sh deploy/zaroz-deploy.sh`.

## Self-managed GitLab

Nothing changes except the registry host, which is already in `CI_REGISTRY_IMAGE`. Zaroz pulls the image from inside its cluster, so the registry must be reachable from the internet (or allow Zaroz's egress addresses).
