GitLab Runner setup starts with the jobs you run, not the autoscaler
Planning GitLab Runner setup means deciding which executor each class of job will use, how much compute the pool can scale to, which tags and scopes route jobs to which runners, and how caching is handled, all while keeping Runner versions aligned with your self-managed GitLab instance. Most teams start by asking how to make the autoscaler work. A better starting point is an inventory of the CI/CD jobs you actually run: what they build, which images and privileges they need, what secrets they read, and how often they run. That inventory determines the executor and the scaling design, and it is far cheaper to produce before any infrastructure exists.
The setup is harder than it looks for reasons that only appear under load. Job isolation depends on the executor: a shell executor runs jobs directly on the host, so one job can affect the next, while Docker and autoscaling executors give each job a fresh environment but still share the host kernel and any privileges you grant. Build images are another hidden decision, because a job that pulls from public registries, or from an internal mirror that is not reachable from every runner, will fail in ways that look like Runner faults. Cache locality matters once autoscaled machines are created and destroyed, since a cache stored only on one instance is lost when that instance goes away. Secrets exposure grows when protected variables are shared with jobs that run on runners less tightly controlled than the rest of the fleet.
The Runner project is licensed under the MIT license, and GitLab Runner supports shell, Docker and autoscaling executors across clouds and hypervisors. That flexibility is useful, but it means the choice is yours to justify. Before you select an executor or cloud target, confirm with your platform team which hypervisor or cloud accounts are approved for build workloads, which network paths the runners need to reach the GitLab instance and artifact storage, and whether any jobs must run in an isolated or air-gapped network. Write these answers down as runner requirements. They will drive the executor choice more than any autoscaling feature will.
Version drift is the failure that tends to appear months after go-live. The Runner documentation advises keeping Runner major and minor versions aligned with GitLab, so every upgrade of the GitLab instance needs a matching plan for the runner fleet, including autoscaling images or templates that embed a Runner version. Ask your implementation partner or internal team to define how runners are upgraded, who approves the change, and how a runner that falls behind is detected and removed from service. Plan these steps alongside your GitLab upgrade calendar rather than treating them as a separate task.
Cost and sizing for runner capacity depend heavily on the workload: job duration, concurrency, build size and peak timing all change the numbers. This page does not give sizing figures or price estimates. The GitLab implementation plan and cost page and the runner-related sections of the cost of ownership page cover how these inputs are gathered and how a fixed-scope quote is structured. Gather your job inventory and peak concurrency estimates before requesting either, because they are the inputs every runner quote depends on.
Which Runner components and settings you will configure
A GitLab Runner design is a chain of decisions: which manager registers with your GitLab instance, how jobs run, where capacity comes from, and which projects may use it. Each component below feeds the next, so settle them in this order before you write any configuration.
Runner manager registration
The Runner manager is the long-running process that registers with your GitLab instance, polls for jobs and hands them to an executor. It is the first thing you install and the component most tied to your GitLab version. Check which host runs it, how its registration token is stored, and how it reaches your GitLab instance on your network, including any air-gapped path you need.
Executor choice
The executor defines where each job runs: directly on the host through the shell executor, in containers through the Docker executor, or in pods through the Kubernetes executor. This choice decides job isolation, what build tools jobs can rely on and how much host access a job receives. Confirm which executors your workloads need before you commit to an autoscaling design.
Autoscaling connector
An autoscaling connector lets the Runner create and remove machines on cloud or hypervisor capacity as job demand changes. It determines how instances are provisioned, what images they boot from and who holds the credentials to create them. Ask your platform team which cloud or hypervisor APIs they can grant, and which machine images they already maintain and patch.
Runner tags
Tags label each Runner with the capabilities it offers, such as an operating system, a GPU, or access to a specific network zone. Jobs request tags in the pipeline definition, so tags route work to the right capacity. Keep the tag vocabulary short and documented, because a missing or misspelled tag leaves jobs waiting with no error on the Runner side.
Runner scope
Scope controls which projects may use a Runner: the whole instance, a group, or a single project. Narrow scope keeps sensitive workloads and secrets away from unrelated teams, while instance scope suits shared build capacity. Map your repositories to trust zones before you register Runners, and review scope again when teams move between groups.
Cache configuration
Cache settings control where dependency and build caches are stored and how they are shared between jobs and Runners. With autoscaling, instances are often replaced, so a cache that lives only on one machine gives inconsistent results. Decide whether caches go to shared object storage, who can read them, and how long they are kept, and confirm the storage target and retention with your team.
Which GitLab Runner executor fits your jobs
This table compares the shell, Docker and autoscaling executors, including Kubernetes-based capacity, across isolation, tooling consistency, host access, cleanup, caching and patching responsibility. Read each row as a question to answer for your own jobs, then choose the executor whose operational cost your team can carry. Most teams end up running more than one executor, separated by runner tags.
| Decision factor | Shell executor | Docker executor | Autoscaling executor (cloud or hypervisor) |
|---|---|---|---|
| When it fits | Few trusted jobs that need direct access to a specific host, device or licensed tool | Most standard build, test and packaging jobs where the image defines the toolchain | Bursty or high-volume pipelines where idle runner capacity should scale to zero |
| Job isolation | Jobs share the host user space and can affect each other and the host | Each job runs in a container, with isolation limited by the container runtime and configuration | Each job usually runs in a fresh machine or container, so state rarely carries over |
| Build tool consistency | Depends on what is installed on each host, so drift is likely over time | Set by the image each job pulls, so versions are pinned in code | Set by the image or machine template, so consistency depends on how it is built and updated |
| Host access | Full access to the runner host, which is useful and also a risk | Limited to mounted volumes and configured devices | Limited to the instance or pod, with access to the cloud or hypervisor API held by the runner |
| Cleanup burden | High: workspaces, caches and leftover processes must be managed on the host | Moderate: unused images, volumes and build caches need periodic pruning | Lower per job, since instances or pods are discarded, but image and template maintenance remains |
| Cache behavior | Local to the host, which is fast but can leak between projects if not scoped | Local to the runner or a shared cache store, so configuration decides reuse | Often needs a shared object store, because ephemeral machines do not keep local caches |
| Who patches the build environment | Your team, for the host and every tool installed on it | Your team for the runner host and base images, and image owners for toolchains | Your team for machine or pod templates and the autoscaler, plus image owners for toolchains |
| Operational skill needed | Basic Linux administration and careful access control | Container runtime administration and image registry management | Cloud or hypervisor automation, capacity limits and autoscaler monitoring |
| Kubernetes-based capacity | Not applicable | Can run on cluster nodes, but cluster and node security become part of runner security | Pods scale with cluster capacity, so node pool limits and resource requests shape job queues |
How to build GitLab Runner autoscaling in the right order
This sequence takes a self-managed GitLab instance from an unplanned pool of build machines to tagged, autoscaling Runners with predictable caching and pinned build images. Each step ends with a check so that a failure is found before the next layer is added.
Confirm the GitLab and Runner version pairing
Before installing anything, the platform owner records the running GitLab version and selects a GitLab Runner version whose major and minor numbers match it, as the Runner documentation advises. Check the current GitLab version in the Admin Area, then check the Runner release notes for compatibility. Write the chosen pair into your change record so every later upgrade starts from a known state. Done when the instance version and the Runner version are documented together and the pair is listed in your upgrade plan, which must also account for GitLab's required upgrade stops.
Define job categories and runner tags
Group your pipelines by what they need from a machine: for example, lightweight linting, standard builds, builds that need GPUs or large memory, and jobs that must touch restricted networks. Each category becomes a tag set that jobs request in .gitlab-ci.yml. Agree the tag names with the teams that own pipelines, because renaming tags later means editing every affected pipeline. Done when a written tag catalogue exists with one owner per tag and a note on which projects or groups may use each tag.
Choose the executor for each category
Match each tag to an executor. The shell executor runs jobs directly on the host and offers the least isolation. The Docker executor runs each job in a container and is the common default for general builds. Kubernetes and autoscaling executors suit variable demand, and the autoscaling option is the one that provisions cloud or hypervisor capacity on demand. Record the isolation trade-off for each category, especially for jobs that handle protected variables or production credentials. Done when every tag has one named executor and a documented reason for it.
Provision the manager host or cluster
The platform or DevOps team builds the host or cluster that runs the Runner manager, inside your own cloud account, data center or isolated network. Keep it on a patched operating system with time synchronization, restricted inbound access and outbound access only to your GitLab URL and the cloud or hypervisor API. Confirm the sizing and any network path with the Runner documentation rather than assuming defaults. Done when the host can reach the GitLab instance and the capacity API, and its access is limited to the addresses and ports you listed.
Register the Runner with the right scope
Register the manager with GitLab at the scope the category needs: instance, group or project. Instance runners serve every project, so reserve them for general-purpose tags and avoid giving untrusted projects access to runners that hold sensitive capacity. Protected runners suit deployment jobs. Confirm the registration arguments and tag options in the Runner documentation for your Runner version. Done when each runner appears in the GitLab runner list with the expected scope, tags and lock setting, and a test job from an unauthorized project does not pick it up.
Configure the autoscaling connector
Connect the manager to the cloud or hypervisor that will create and remove worker machines. Create a dedicated service identity with only the permissions needed to launch, tag and terminate those machines, and store its credentials in a secrets manager rather than in plain configuration files. Set the maximum number of workers, the idle timeout and the machine image reference, then confirm the exact key names in the Runner autoscaling documentation. Done when a job queued under the tag starts a new worker, the worker runs the job, and the worker is removed after the idle timeout you set.
Set up cache storage
Configure a shared cache location so that dependency downloads survive between jobs and between autoscaled workers, which are otherwise short-lived. Choose between a distributed cache in object storage and a local cache on a persistent host, based on where your workers run and how much data they move. Keep cache keys scoped so that jobs in one project cannot read another project's cache entries. Done when two consecutive jobs on the same tag show a cache hit in their logs, and a job from a second project does not read the first project's cache.
Pin build images
Replace floating image tags such as latest in build and test jobs with specific, version-pinned references, and store approved images in a registry you control. Pinning makes results reproducible and keeps a patched image from changing a pipeline without review. Agree a process for updating pins, including who approves a new image and how the change is tested. Done when a scan of the pipeline definitions finds no unpinned images in templates used by the tagged categories, and the registry holds the approved versions.
Run a test pipeline for each tag
Run a test pipeline per tag that prints the runner name, executor, host and tag set, then performs a cache write and read. Confirm that each job lands on the intended runner, that the autoscaler creates and removes capacity as expected, and that protected runners reject jobs from unprotected branches. Keep the test pipeline in version control so it can be rerun after every upgrade. Done when each tag has a passing run, the logs show the expected runner and scope, and the results are recorded with the version pair from step one.
How Runner design changes on VMs, Kubernetes and air-gapped networks
A single Runner manager host with fixed capacity is the simplest model. One Linux or Windows host runs the GitLab Runner manager and executes jobs directly or in containers on the same machine, so capacity is whatever the host can supply. Your team owns the host end to end: patching, the Runner binary, the configuration file, registration tokens and disk cleanup. Nothing scales on its own, so queue time grows as pipeline load grows.
- Manager ownership: one host, owned by your platform or infrastructure team, with no fleet to orchestrate.
- Scaling trigger: none. Someone adds capacity by hand, usually by adding a host or raising concurrency in the configuration.
- Provision in advance: the host sized for peak concurrent jobs, a container runtime if you use the Docker executor, and local disk for caches and job artifacts.
- Watch for: build caches filling the disk, and jobs sharing a host that should be isolated from one another.
Before you commit, confirm with your team how long a backlog of queued jobs is acceptable at your busiest hour, and who restarts the host when it fails.
With autoscaling on cloud compute, the manager stays small and long-lived while it creates and removes virtual machines through the cloud provider's API. Each job runs on a fresh machine, which limits the damage a malicious or broken job can do. Your team owns the manager, the machine image used for workers, the cloud account permissions and the autoscaling settings. Cost follows usage, but the cloud bill, network egress and image maintenance become recurring line items.
- Manager ownership: one manager host you run, with credentials scoped to create and delete worker instances in named subnets.
- Scaling trigger: pending jobs and the configured idle count. The manager requests instances when jobs wait and removes them after an idle period.
- Provision in advance: a worker image with the required tooling, network paths to your GitLab instance and package sources, a dedicated cloud account or project, and instance quotas that cover peak concurrency.
- Watch for: provider quotas and API rate limits that stall scale-up, and worker images that drift from the Runner version.
Ask the implementation partner to show the scaling settings against your expected peak, and to name the quota increases your cloud account needs before go-live.
Autoscaling on a hypervisor follows the same pattern as cloud autoscaling, but the worker machines are created on your own virtualization platform in your data center. The manager calls the hypervisor's API to clone or start worker VMs from a template. Your team owns the hypervisor, the templates, storage capacity and the manager. Capacity is bounded by hardware you have already bought, so the decision moves from a usage bill to a question about host headroom.
- Manager ownership: the manager and the hypervisor integration are owned by your virtualization or platform team, with a service account limited to the Runner worker pool.
- Scaling trigger: pending jobs against the configured maximum and idle count, bounded by free compute and memory on the hypervisor cluster.
- Provision in advance: a golden template for workers, a dedicated resource pool or cluster capacity, storage for clones, and network segments that reach GitLab and internal package sources.
- Watch for: template drift after OS patches, and scale-up that fails silently when the cluster runs out of memory.
Confirm with your virtualization team which hypervisor integration your Runner version supports, and how template patching will be scheduled so workers never run stale images.
Kubernetes-hosted Runners use the Kubernetes executor, so each job runs as a pod in a cluster. This suits teams that already operate Kubernetes and want to run GitLab Runner next to a GitLab deployment, including one installed with the official Helm chart. Your platform team owns the cluster, the Runner deployment, namespaces, RBAC and resource limits. Scaling comes from the cluster scheduler rather than from a separate autoscaling fleet, so the cluster autoscaler and resource quotas decide how many jobs run at once.
- Manager ownership: the Runner deployment sits in the cluster, owned by the platform team, with a service account bound to job namespaces only.
- Scaling trigger: job demand creates pods, and the cluster autoscaler adds nodes when pods cannot be scheduled.
- Provision in advance: namespaces, resource requests and limits for job pods, a registry or image mirror, persistent cache volumes if you use them, and network policies that allow jobs to reach GitLab.
- Watch for: the Runner keeping stateful GitLab components off the cluster. GitLab's reference architectures do not support running PostgreSQL or Redis inside Kubernetes, so keep those on VMs or managed services.
Ask for the Kubernetes version and Runner chart version that match your GitLab release, and confirm how privileged job pods will be restricted in your cluster.
What drives GitLab Runner autoscaling effort and how to contain it
This table lists the factors that expand the planning, build and run effort for self-managed GitLab Runners, with how each one raises effort and how to keep it contained. Read each row as a lever: the fewer distinct environments, tags and custom images you carry, the less configuration and evidence work you inherit. Runner compute cost is covered separately on the cost of ownership page.
| Driver | How it raises effort | How to keep it down |
|---|---|---|
| Teams and job categories | Each team with its own job types tends to request its own runner tags, scopes and capacity pools, multiplying autoscaling configurations to design, test and monitor. | Agree a short tag vocabulary across teams, consolidate job categories that need the same executor and image, and confirm which team owns each runner group before build starts. |
| Distinct build environments | Every separate operating system, language toolchain or executor type (shell, Docker, Kubernetes) adds a runner profile with its own autoscaling rules, images and troubleshooting path. | Standardize on a small number of executor types, choose one primary base image family, and retire profiles that only one legacy project uses. |
| Projects needing privileged builds | Jobs that need privileged containers, host devices or Docker-in-Docker push the runner into a higher-trust tier that needs separate isolation, network rules and review. | Limit privileged runners to named projects, prefer rootless or daemonless build approaches where your workloads allow, and keep privileged runners out of the shared pool. |
| Cache size and locality | Large dependency caches stored away from the runner host slow jobs, drive up storage and bandwidth needs, and make autoscaled instances behave differently depending on where they start. | Confirm the cache storage backend early, set expiry and size rules, and decide whether caches are shared across runners or kept per runner group. |
| Environments (dev, test, production deploy) | Keeping separate runner pools for development, testing and production deployment means more instances, more credentials to rotate and more access paths to document. | Separate deployment runners from build runners at minimum, restrict production runners to protected branches, and reuse the same configuration templates across non-production pools. |
| Compliance evidence requirements | Assessors often ask who can register runners, how runner images are approved and how job logs and access are retained, which adds documentation and recurring evidence collection. | Define runner registration rules, image approval steps and log retention once, then map them to the controls your assessor uses. DeployOpen prepares evidence but does not issue audit reports or certifications. |
| Amount of custom image work | Each custom image needs a build pipeline, scanning, patching cadence and testing against new GitLab Runner versions, so the image count drives ongoing maintenance. | Start from a small set of maintained base images, add layers only for real toolchain gaps, and set a review date for each custom image so unused ones are removed. |
How to keep Runners from exposing secrets and the host
A GitLab Runner executes code from every project it serves, so the Runner itself is part of the trust boundary. The controls below cover how registration credentials, build hosts, tags and caches are scoped so that one project's job cannot read another project's secrets or take over the machine it runs on. Instance-wide settings are covered in GitLab's hardening documentation.
Registration token handling and rotation
The registration token and the authentication token written to config.toml let a machine join GitLab as a Runner. Store them in your secrets manager, not in provisioning scripts or images, and limit who can read the manager host. Plan a rotation procedure before go-live: confirm in GitLab how a Runner is unregistered and re-registered, and confirm how quickly stale tokens stop working in your version. Ask whether any registration tokens remain in CI variables or job logs.
Separating runners by trust level
Create distinct Runner groups for untrusted contributors, internal projects and deployment work that holds production credentials. A Runner registered to a group or project serves only that scope, so a fork or a low-trust project never reaches the machines that hold deployment access. Confirm that each Runner's scope matches the trust level of the projects it is meant to serve, and review scopes when a project changes ownership or sensitivity.
Avoiding privileged container modes on shared hosts
The privileged flag on the Docker executor gives containers near-host access, so a job that runs in privileged mode can affect other jobs on the same machine. Reserve privileged mode for dedicated Runners serving trusted projects, and handle Docker-in-Docker needs with a dedicated build host rather than a shared pool. Ask your team to list every job that requires privileged mode and to confirm which Runner serves it.
Ephemeral build hosts
With the autoscaling executors, each job or small batch can run on a machine that is created for it and removed afterward, so no residue from earlier jobs remains. Confirm that your autoscaler removes instances on completion, that no long-lived credentials are baked into the machine image, and that the instance role or service account has only the cloud permissions the job needs. Persistent hosts should be treated as shared and reviewed accordingly.
Limiting which projects use which tags
Runner tags route jobs to machines, and a tag that a sensitive Runner accepts is only protected if jobs without access cannot request it. Use protected Runners and restrict them to protected branches or tags where the Runner's purpose requires it. Review the tag list for each Runner, remove tags that no longer have a purpose, and confirm that no broadly available tag points to a deployment-capable machine.
Treating cache contents as shared data
Runner caches can be shared between jobs that use the same cache key, so a cache written by one job may be read by another. Do not place secrets, tokens or signed artifacts in caches, and use cache keys that separate branches or projects when their trust levels differ. Ask whether your cache store is reachable from more than one Runner scope, because a shared cache bucket can cross the boundaries your Runner groups enforce.
Protecting the manager configuration file
The Runner manager's config.toml holds registration credentials, cloud access settings and executor configuration. Restrict file permissions to the service account, keep it out of configuration management repositories that many people can read, and back it up with the same care as other secrets. After any change, check the file for leftover tokens and confirm that logs do not print its contents. Record who can change it, since edits alter which projects a host serves.
How to test that Runner autoscaling works and fails safely
Verification should prove three things before a Runner pool carries production work: each Runner is registered with the right scope and tags, jobs run only where they are allowed to run, and autoscaling adds and removes capacity without leaving jobs stranded. Start in the GitLab interface or API by confirming that every Runner shows as online, with the expected scope (instance, group or project) and tag list. Then run one small pipeline per tag. Include one job that requests a tag no Runner offers and one that requests a tag held only by a specific pool. The first should stay pending and show a clear pending reason. Record the Runner name, tags, executor and job link for each check.
Next, test the autoscaling behavior itself. Queue more jobs than the idle capacity you configured and confirm that the autoscaler creates instances on your cloud or hypervisor, that the jobs start and that they finish. Then let the pool sit idle and confirm that instances are removed after the idle period set in the Runner configuration. Read that value from your configuration file before the test so you know what result to expect. For caching, run two consecutive jobs in the same project that use the same cache key, check the second job log for a cache restore, and repeat the check on a different instance to confirm where the cache is read from. Confirm the cache storage location with your own setup, since it depends on how you configured caching.
Failure tests show whether the setup fails safely. Stop the manager process in a test environment and submit jobs. Confirm that new jobs wait visibly with a reason rather than appearing to run, and that jobs already running either finish or fail with a clear message. Exhaust the cloud quota or instance limit in a test account and check that the autoscaler logs the error and that queued jobs remain pending with an explanation. Revoke a Runner authentication token and confirm that the Runner stops accepting jobs and that the revocation appears in the administration view. Terminate an instance during a job and confirm that the job ends through its timeout or failure path instead of running indefinitely. Check the timeout values before the test.
For each failure test, observe whether an alert fired in your monitoring or notification system, and note the time and the recipient. Confirm alert rules with your monitoring team, because GitLab does not decide what your organization is alerted on. Record every result as acceptance evidence in a table with columns for test, steps, expected result, observed result, alert fired (yes or no), timestamp, tester, GitLab version and Runner version. Keep the Runner major and minor versions aligned with GitLab, as the Runner documentation advises, and record both versions in each row. Repeat the full set after any upgrade, configuration change or change to cloud limits, and keep the records with your change history.
How to keep Runner versions aligned with GitLab after go-live
A Runner fleet drifts from its GitLab instance quietly, so the work after go-live is a repeating cycle of version tracking, health checks and housekeeping. The cards below cover the version rules, the release calendar, and the routine tasks that keep jobs running and recoverable.
Match Runner major and minor versions
GitLab Runner's documentation advises keeping the Runner's major and minor versions aligned with the GitLab instance it serves. Check the Runner version on every host and autoscaling image before and after each GitLab upgrade, because a mismatch can surface as job failures that look unrelated to the upgrade. Patch level differences are the lower-risk gap, but confirm the expected range against the Runner documentation for your release.
Track the monthly release calendar
GitLab ships a minor release on the third Thursday of each month and patch releases twice a month, according to its maintenance policy. Assign one person to read each release's notes for Runner changes and to schedule Runner updates in the same window as instance updates. A written calendar of expected dates lets the team plan changes in advance rather than reacting to every release.
Plan around the supported release window
GitLab currently maintains 19.5, 19.4 and 19.3. Bug fixes are backported only to the current stable release, so a fleet on an older minor will not receive them, while security fixes reach the previous two monthly releases. Decide in advance which versions your organization will run, and treat falling outside the supported window as a risk that needs a dated upgrade plan.
Schedule required upgrade stops
GitLab requires upgrades to pass through required stops at x.2, x.5, x.8 and x.11, such as 19.5 and 19.8. Map these stops against the GitLab version you run today and the target you plan to reach, then align Runner upgrades to the same checkpoints. Confirm the current list in the GitLab upgrade paths documentation, since the GitLab instance sequencing itself is covered on the upgrades page.
Monitor queue time and failed jobs
Queue time and job failure rates are the earliest signals that a Runner has drifted, lost capacity or hit a cloud quota. Set alerts on queued jobs waiting beyond the time your teams consider acceptable, and review failed jobs grouped by Runner and executor. Ask whether the Runner metrics endpoint and your chosen monitoring stack can expose these values before go-live.
Rebuild images and clean caches
Autoscaling and Docker executors depend on base images and cache volumes that age out of date. Set a rebuild cadence for the images used by runners, tied to the operating system patch cycle and to the tools your pipelines install, and record who approves each rebuild. Schedule cache cleanup so disk usage stays bounded on runner hosts and cloud volumes, and confirm the cleanup does not remove caches that long-running pipelines still need.
Back up Runner configuration and credentials
The Runner configuration file holds registration tokens, executor settings and cloud credentials for autoscaling, and losing it means re-registering every Runner. Store the configuration and its secrets in a protected location separate from the GitLab backup, and test that a replacement host can be registered from the saved copy. Rotate the credentials on a schedule and record each rotation.
Document tags, scopes and owners
Runner tags and scopes decide which projects can use which capacity, so an undocumented tag quickly becomes a dependency nobody can change. Keep a register that lists each tag, the executor and hosts behind it, the projects allowed to use it and a named owner who approves changes. Review the register at each release cycle, and remove tags that no pipeline references.
What buyers ask before setting up GitLab Runner autoscaling
How much effort does GitLab Runner autoscaling setup take?
Effort depends mostly on how many runner types you need, where the capacity comes from and how much your pipelines rely on custom images and caching. One Docker executor pool in a single cloud account is a much smaller job than shell, Docker and Kubernetes runners spread across cloud and hypervisor capacity with separate scopes.
Main drivers:
- Number of executors, runner tags and scopes
- Autoscaling target: cloud, hypervisor or Kubernetes capacity
- Image and cache design, including shared cache storage
- Secrets handling and network egress rules
- Number of projects and groups sharing each runner
When you request a quote, ask for the runner types in scope, the capacity assumptions and the test jobs used to prove that scaling works.
Can our team set up GitLab Runner autoscaling without a partner?
Yes, a team with CI/CD and cloud operations experience can build it in-house. The runner software is MIT-licensed, so the decision turns on the surrounding design: autoscaling limits, instance images, cache placement, isolation between projects and tested recovery when a node disappears during a job.
Before deciding, check three things. Is there an owner for runner upgrades on a fixed schedule? Has anyone run autoscaling in production before? Can the team generate representative job bursts to test scaling? If any answer is no, a scoped partner engagement can cover the build and hand over runbooks, or the team can start with one executor pool and expand once it has operating experience. Confirm the choice against your pipeline volume and staffing.
What does the setup need from our platform and cloud teams?
The work needs access and decisions from both teams before the build starts. Platform engineering typically provides the GitLab instance address, the runner registration or authentication path, network routes from runner hosts to GitLab and to package and image registries, and the Kubernetes cluster or hypervisor API if that is the capacity source.
Cloud teams provide the account or project for instances, machine images or templates, least-privilege instance roles, fleet or autoscaling group limits, quotas and storage for caches. Both teams should agree on who approves new runner tags, how secrets reach jobs and how logs are retained. Ask for a list of each access request, its owner and its expected lead time. Approval lead times often set the pace of the project more than the build does.
How long does a GitLab Runner autoscaling setup take?
Duration follows scope. A single executor pool in an existing cloud account moves faster than a multi-executor design that spans clouds, hypervisors and Kubernetes. The main schedule drivers are approval lead times for accounts and network changes, the number of runner types and scopes, how many pipelines must be migrated or tested, and how much cache and image work is needed.
Ask for a timeline tied to the written scope before accepting any generic estimate. A useful schedule lists milestones: access confirmed, first runner registered, autoscaling proven under load, and handover of runbooks and the upgrade plan. Ask which milestones the quote covers and which depend on your teams. A partner should also state how the schedule changes if an approval slips.
Does a fixed-scope engagement suit a GitLab Runner setup?
Yes, a fixed-scope engagement suits a bounded runner build, provided the scope names the executors, capacity sources, pipelines and handover items. Most DeployOpen engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.
This fits runner work well when the boundaries are clear: a defined number of runner types, a named cloud or hypervisor target and a test plan for scaling. Scope changes usually come from unplanned pipelines, new compliance requirements or capacity sources added mid-project. Ask how change requests are priced and approved, what the handover includes, and whether ongoing upgrades belong in a separate retainer. Ongoing support, upgrades and continuous testing are available as retainers.
How are GitLab Runner upgrades handled after go-live?
Runner upgrades follow the GitLab release cadence, and the GitLab Runner documentation advises keeping Runner major and minor versions aligned with GitLab. GitLab ships a minor release on the third Thursday of each month and patch releases twice a month, and its maintenance policy backports security fixes to the previous two monthly releases as well as the current stable release.
Plan a runner upgrade step after each GitLab minor upgrade, test it in a non-production environment and confirm the version pairing before rolling runners to production. Ask who performs the upgrade, how autoscaled instances pick up a new runner version and how rollback works if jobs fail after an upgrade. Required GitLab upgrade stops, such as x.2, x.5, x.8 and x.11 since GitLab 17.5, apply to the GitLab server and should be checked in the same plan.
Which executor should autoscaling runners use for our jobs?
Choose the executor based on what your jobs need to run. Shell executors run jobs directly on the host, so they share state across jobs and offer weak isolation. Docker executors run each job in a container and are a common choice for build and test work on autoscaled cloud or hypervisor capacity. Kubernetes executors schedule job pods on a cluster you already operate, which suits teams that run Kubernetes well.
Tags, cache design and image management should be decided with the executor, not after it. Confirm the autoscaling mechanism for your chosen executor in the GitLab Runner documentation before finalizing the design, because the supported options differ by executor. Ask your partner, or your own team, to show which jobs run on which executor and how each one is isolated.
How DeployOpen works on this
DeployOpen plans, implements and documents self-managed GitLab Runner fleets inside your own cloud accounts, data centers or isolated networks. Work is agreed as a fixed-scope project in writing before it starts, and ongoing support is available as a retainer.
How we scope
- The written scope names which executors are in use: shell, Docker or Kubernetes, with the reasons for each.
- It defines the autoscaling target, whether cloud or hypervisor capacity, and the maximum concurrent job count to plan for.
- It lists runner tags, scopes, protected branch access and the groups or projects each runner serves.
How we build
- Runner manager and worker images are built, pinned and configured, with Runner versions aligned to your GitLab version.
- Autoscaling settings, capacity limits, idle policies and cache storage are configured and tested against representative pipeline jobs.
- Runner isolation, registration tokens and handling of protected variables are configured to match your security requirements.
How we hand over
- You receive architecture and configuration documentation covering each executor, autoscaling rule and tag or scope mapping.
- Runbooks cover adding or removing runners, rotating tokens, upgrading Runner versions and responding to stuck or failed jobs.
- Handover includes test results from pipeline runs, so your team can confirm the fleet behaves as specified before go-live.
How we support
- Ongoing support can cover Runner version alignment with each GitLab minor release, as agreed in the retainer scope.
- Capacity and cache reviews can be scheduled so autoscaling limits keep pace with changes in pipeline volume.
- Support scope, response expectations and coverage hours are confirmed in writing before a retainer begins.

