Starting GitLab Runner in Yandex Serverless Containers
In this tutorial, you will learn to start GitLab Runner
To start GitLab Runner in Serverless Containers:
- Get your cloud ready.
- Create GitLab Runner and get a token.
- Create a secret.
- Create service accounts.
- Create the service account API key.
- Create a container.
- Set up a webhook in GitLab.
If you no longer need the resources you created, delete them.
How it works
Once a job is created, GitLab sends a webhook request to the new Serverless Containers container. The container runs based on a public image from Yandex Container Registry and gets the necessary secrets from Yandex Lockbox. GitLab Runner deploys inside the container, connects to GitLab and takes the job for execution, runs it in an isolated Docker container, and shuts down when done. This approach eliminates the need to keep VMs running continuously: you pay only for actual job execution time.
You can check the source code in the SourceCraft repository
Key steps
-
The service receives HTTP requests on port
PORT(8080by default) and atWEBHOOK_PATH(/by default). -
If necessary, it checks the secret in the
X-Gitlab-Tokenheader using theGITLAB_SECRETenvironment variable. -
The only requests that are processed are those headed
X-Gitlab-Event: Job Hook. -
The
build_statusfield is extracted from the request body. If it is set topending, the service runs this command:gitlab-runner run-single \ --url CI_SERVER_URL \ --token RUNNER_TOKEN \ --wait-timeout WAIT_TIMEOUT \ --executor docker \ --docker-privileged \ --max-builds MAX_BUILDS -
When running a container, the service also does the following:
- If needed, mounts
cgroup v2. - Prepares the
/runand/var/lib/dockerfolders. - Starts the built-in
dockerdand waits till it is ready (DOCKERD_READY_TIMEOUT).
- If needed, mounts
How it works inside the container
Inside the container, the service starts an HTTP server and a GitLab Runner process, which then takes a new CI/CD job from GitLab and executes it in separate isolated docker containers.
Environment variables
The service uses the following environment variables:
| Variable | By default | Required | Description |
|---|---|---|---|
RUNNER_TOKEN |
— | Yes | GitLab Runner token (project/group/instance) |
CI_SERVER_URL |
https://gitlab.com |
No | GitLab CI address |
PORT |
8080 |
No | HTTP port |
WEBHOOK_PATH |
/ |
No | Webhook endpoint path |
GITLAB_SECRET |
— | No | Secret for checking the X-Gitlab-Token header |
WAIT_TIMEOUT |
10 |
No | Value for gitlab-runner --wait-timeout, in seconds |
MAX_BUILDS |
1 |
No | Value for gitlab-runner --max-builds |
DOCKERD_READY_TIMEOUT |
5s |
No | Timeout until dockerd is ready (time.Duration) |
Restrictions
- This tutorial uses the Docker executor only. You can build a container yourself with required dependencies and use the Shell executor in it.
- Each webhook request starts a separate ephemeral runner. The state between runners is not retained.
- Jobs should have tags that match the runner’s tags. If a
jobin.gitlab-ci.ymlhas no matching tags, the runner will not take it. Use theRun untagged jobsoption when creating a runner for it to take all jobs.
Get your cloud ready
Sign up for Yandex Cloud and create a billing account:
- Navigate to the management console
and log in to Yandex Cloud or create a new account. - On the Yandex Cloud Billing
page, make sure you have a billing account linked and it has theACTIVEorTRIAL_ACTIVEstatus. If you do not have a billing account, create one and link a cloud to it.
If you have an active billing account, you can navigate to the cloud page
Learn more about clouds and folders here.
Required paid resources
The infrastructure support cost includes:
- Fee for container invocation count, computing resources allocated to run the application, and outbound traffic (see Serverless Containers pricing).
- Fee for storing secrets (see Yandex Lockbox pricing).
Create GitLab Runner and get a token
- Create a project in GitLab
and open it. - In the left-hand panel, select
Settings → CI/CD. - Open the Runners section and click Create project runner.
- In the Tags field, specify tags for the jobs for this runner to process.
- Click Create runner.
- Under Step 1, copy the runner authentication token and save it.
Create a secret
Create a Yandex Lockbox secret with the runner authentication token.
-
In the management console
, select the folder where you are going to create your infrastructure. -
In the list of services, select Lockbox.
-
Click Create secret.
-
In the Name field, specify the secret name:
gitlab-runner-token. -
Under Secret data:
-
Select the
Customsecret type. -
Add the GitLab Runner token value:
- In the Key field, specify
gitlab_runner_token. - In the Value field, specify the runner authentication token you got earlier.
- In the Key field, specify
-
-
Click Create.
If you do not have the Yandex Cloud CLI installed yet, install and initialize it.
By default, the CLI uses the folder specified when creating the profile. To change the default folder, use the yc config set folder-id <folder_ID> command. You can also set a different folder for any specific command using the --folder-name or --folder-id parameter.
To create a secret, run this command:
yc lockbox secret create \
--name gitlab-runner-token \
--payload '[{"key": "gitlab_runner_token", "text_value": "<runner_token>"}]'
Where:
--name: Secret name.--payload: Contents of the secret provided as a YAML or JSON array.<runner_token>: Runner authentication token you got earlier.
Result:
id: e6qdur7h55vd********
folder_id: b1g681qpemb4********
created_at: "2025-09-16T06:04:12.604Z"
name: gitlab-runner-token
status: ACTIVE
current_version:
id: e6qbaf927irb********
secret_id: e6qdur7h55vd********
created_at: "2025-09-16T06:04:12.604Z"
status: ACTIVE
payload_entry_keys:
- gitlab_runner_token
To create a secret, use the create REST API method for the Secret resource or the SecretService/Create gRPC API call.
Create service accounts
Create two service accounts:
-
gitlab-runner-lockbox-payload-viewerwith thelockbox.payloadViewerrole for access to the Lockbox secret. -
gitlab-runner-callerwith theserverless-containers.containerInvokerrole for the folder. This service account will perform these two functions:- GitLab will use it to invoke the container. The API key will be specified in the
Authorizationheader. - GitLab will use its ID to set jobs for the runner for the container to be invoked asynchronously. The ID will be provided when creating a container revision.
You can split these two functions between two different service accounts as needed.
- GitLab will use it to invoke the container. The API key will be specified in the
-
In the management console
, select Identity and Access Management. -
Click Create service account.
-
Enter a name for the service account:
gitlab-runner-caller. -
Click
Add role and selectserverless-containers.containerInvoker. -
Click Create.
-
Similarly, create the
gitlab-runner-lockbox-payload-viewerservice account without assigning a role to it. -
Assign a role for the secret to the
gitlab-runner-lockbox-payload-viewerservice account:- In the management console
, select Lockbox. - Select the
gitlab-runner-tokensecret. - In the left-hand panel, select
Access bindings. - Click Assign roles.
- Find and select the
gitlab-runner-lockbox-payload-viewerservice account. - Click
Add role and selectlockbox.payloadViewer. - Click Save.
- In the management console
-
Create these service accounts:
yc iam service-account create --name gitlab-runner-lockbox-payload-viewer yc iam service-account create --name gitlab-runner-callerResult:
id: ajenprbpf1s3******** folder_id: b1g681qpemb4******** created_at: "2025-09-16T06:06:13.092790480Z" name: gitlab-runner-lockbox-payload-viewer id: ajetqjm00ji8******** folder_id: b1g681qpemb4******** created_at: "2025-09-16T06:06:15.661704808Z" name: gitlab-runner-caller -
Assign roles to the service accounts:
yc lockbox secret add-access-binding \ --name gitlab-runner-token \ --role lockbox.payloadViewer \ --service-account-name gitlab-runner-lockbox-payload-viewer yc resource-manager folder add-access-binding \ --id <folder_ID> \ --role serverless-containers.containerInvoker \ --service-account-name gitlab-runner-callerWhere:
--name: Secret name.--id: Folder ID.--role: Role being assigned.--service-account-name: Service account name.
Result:
...1s...done (4s) ...1s...done (2s) effective_deltas: - action: ADD access_binding: role_id: serverless-containers.containerInvoker subject: id: ajetqjm00ji8******** type: serviceAccount
To create a service account, use the create REST API method for the ServiceAccount resource or the ServiceAccountService/Create gRPC API call.
To assign the lockbox.payloadViewer role for a secret to the service account, use the updateAccessBindings REST API method for the Secret resource or the SecretService/UpdateAccessBindings gRPC API call.
To assign the service account the serverless-containers.containerInvoker role for the folder, use the updateAccessBindings REST API method for the Folder resource or the FolderService/UpdateAccessBindings gRPC API call.
Create the service account API key
Create an API key for the service account you are going to use to invoke the container. You will need this API key to set up a webhook in GitLab.
- In the management console
, select Identity and Access Management. - Select the
gitlab-runner-callerservice account you created earlier. - In the top panel, click
Create new key and select Create API key. - In the Scope field, select the
yc.serverless.containers.invokescope. - Click Create.
-
Create an API key for the
gitlab-runner-callerservice account:yc iam api-key create \ --service-account-name gitlab-runner-caller \ --scopes yc.serverless.containers.invokeWhere:
--service-account-name: Service account name.--scopes: Key scope.
Result:
api_key: id: aje3oa26mt52******** service_account_id: ajetqjm00ji8******** created_at: "2025-09-16T06:08:47.018697312Z" scope: yc.serverless.containers.invoke scopes: - yc.serverless.containers.invoke secret: AQVNw8KEa3SiA8vlpXEhfl9k-uYT22Ws********
To create an API key, use the create REST API method for the ApiKey resource or the ApiKeyService/Create gRPC API call.
Save the secret key you got: you will not be able to get it a second time.
Create a container
Create a container and a container revision with a Docker image to start GitLab Runner.
Note
In our example, we mount an ephemeral disk at /mnt to expand the available space in the root file system for the period of executing the container. Docker stores data in /var/lib/docker: this location becomes available thanks to the root file system expansion.
-
In the management console
, select Serverless Containers from the list of services. -
Click Create container.
-
Enter a name for the container:
serverless-gitlab-runner. -
Click Create.
-
Navigate to the Editor tab.
-
Under Resources, specify the required RAM, e.g.,
1024 MB. -
Under Image settings:
-
Click Enter link, and specify
cr.yandex/yc/serverless/gitlab-runnerin the Image URL field. -
In the Environment variables field, add the following variables:
CI_SERVER_URL:https://gitlab.comWEBHOOK_PATH:/webhook
-
In the Lockbox secrets field, specify:
- Environment variable:
RUNNER_TOKEN. - Secret ID:
gitlab-runner-token. - Version ID: Current version ID.
- Secret key:
gitlab_runner_token.
- Environment variable:
-
-
Under Settings:
- In the Service account field, specify
gitlab-runner-lockbox-payload-viewer. - In the Timeout field, specify the required value, e.g.,
600 seconds.
- In the Service account field, specify
-
Under Mounted ephemeral disk:
- Click Add ephemeral disk.
- In the Mount path field, specify
/mnt. - In the Disk size field, specify the required value, e.g.,
10 GB.
-
Under Asynchronous call:
- Click Enable.
- In the Service account field, select
gitlab-runner-caller.
-
-
Click Create revision.
-
Create a container:
yc serverless container create --name serverless-gitlab-runnerResult:
id: bba83i1mrb5s******** folder_id: b1g681qpemb4******** created_at: "2025-09-16T06:10:03.153Z" name: serverless-gitlab-runner url: https://bba83i1mrb5s********.containers.yandexcloud.net/ status: ACTIVE -
Create a container revision:
yc serverless container revision deploy \ --container-name serverless-gitlab-runner \ --runtime=http \ --cores <number_of_cores> \ --memory <RAM_size> \ --image cr.yandex/yc/serverless/gitlab-runner \ --environment CI_SERVER_URL=https://gitlab.com \ --environment WEBHOOK_PATH=/webhook \ --secret id=<secret_ID>,version-id=<secret_version_ID>,key=gitlab_runner_token,environment-variable=RUNNER_TOKEN \ --service-account-id <service_account_1_ID> \ --execution-timeout <timeout> \ --mount type=ephemeral-disk,mount-point=/mnt,size=<disk_size>\ --async-service-account-id <service_account_2_ID>Where:
-
--container-name: Container name. -
--runtime: Operating mode. -
--cores: Number of cores available to the container, e.g.,1. -
--memory: Required memory, e.g.,1GB. -
--image:gitlab-runnerDocker image URL. -
--environment: Environment variables:CI_SERVER_URL: GitLab CI address.WEBHOOK_PATH: Webhook endpoint path.
-
--secret: Lockbox secret. -
--service-account-id:gitlab-runner-lockbox-payload-viewerservice account ID. -
--execution-timeout: Timeout, e.g.,600s. -
--mount: Ephemeral disk mounting parameters:type=ephemeral-disk: Type of the file system being mounted.mount-point: Name of the mount point. The directory the disk will be mounted to will be available at/mnt.size: Ephemeral disk size in GB, e.g.,10GB.
-
--async-service-account-id:gitlab-runner-callerservice account ID.
Result:
id: bba27hejd69a******** container_id: bba83i1mrb5s******** created_at: "2025-09-18T09:38:14.528Z" image: image_url: cr.yandex/yc/serverless/gitlab-runner image_digest: sha256:ac62... environment: CI_SERVER_URL: https://gitlab.com WEBHOOK_PATH: C:/Program Files/Git/webhook resources: memory: "1073741824" cores: "1" core_fraction: "100" execution_timeout: 600s concurrency: "1" service_account_id: ajenprbpf1s3******** status: ACTIVE secrets: - id: e6qdur7h55vd******** version_id: e6qbaf927irb******** key: gitlab_runner_token environment_variable: RUNNER_TOKEN log_options: folder_id: b1g681qpemb4******** mounts: - mount_point_path: /mnt ephemeral_disk_spec: size: "10737418240" block_size: "4096" runtime: http: {} async_invocation_config: service_account_id: ajetqjm00ji8********Tip
In case of the
mounts[0].mount_point_path: Field does not match the pattern /[-_0-9a-zA-Z/]*/error, escape the mount point name:mount-point=//mnt -
To create a container, use the create REST API method for the Container resource or the ContainerService/Create gRPC API call.
To create a container revision, use the deployRevision REST API method for the Container resource or the ContainerService/DeployRevision gRPC API call.
Set up a webhook in GitLab
-
Open your project in GitLab
. -
In the left-hand panel, select
Settings → Webhooks. -
Click Add new webhook.
-
In the URL field, specify the Serverless Containers public endpoint:
https://<container_ID>.containers.yandexcloud.net/webhook. -
Under Custom headers, click Add custom header and add the following ones:
Header name Header value Description AuthorizationApi-Key <API_key>gitlab-runner-callerservice account API key. Required if the container is private; otherwise, the request will be denied. Learn more in Authenticating when invoking a private container via HTTPS.X-Ycf-Container-Integration-TypeasyncAsynchronous container invocation. The platform will immediately return respond to the invocation with 202. -
Under Trigger, make sure the
Job eventsoption is enabled. -
Click Add webhook.
After that, you will be able to use serverless GitLab Runners.
Tip
You can additionally configure GitLab Runner using environment variables and flags.
View the available options by running the gitlab-runner run-single -h command. You can also look up the guide section on the GitLab Runner commands
How to delete the resources you created
To stop paying for the resources you created: