GitLab Runner Autoscaling with Docker Machine and Fleeting
AI generated
CI/CD
.yml
GitLab · CI/CD · Infrastructure
Runner Autoscaling with
Docker Machine and Fleeting

Fluctuating build load is the norm: barely any pipelines at night, a flood of merge request pipelines in the afternoon. Static runner provisioning either wastes money or produces queue times, autoscaling solves both.

18 min read Docker Machine Fleeting Autoscaling Cost Control

1. Why fixed runner capacity almost always has the wrong size

A fixed-size pool of runner machines inevitably has to be sized for peak load, otherwise developers wait in the job queue for a long time during busy hours. That peak load, however, typically occurs for only a few hours a day, for example when many teams trigger merge request pipelines at the same time. Outside those windows, at night or on weekends, the same machines sit largely idle while still incurring the full infrastructure cost.

Autoscaling breaks this dilemma by spinning up runner machines only when actually needed and tearing them down automatically after an idle period. Instead of permanently keeping a fixed number of machines around, only a small base capacity stays up at all times, while additional capacity is added elastically according to pipeline volume. For cloud-based runners this translates directly into cost savings, since only actually-used compute time gets billed.

2. The Docker Machine model: how it historically worked

The Docker Machine executor was, for many years, the standard way to autoscale GitLab runners. It used the Docker Machine tool, since discontinued by Docker itself, to provision new virtual machines with a cloud provider like AWS, GCP, or Azure on demand, then installed Docker on them and started a job container. The runner itself ran as a kind of manager process, deciding based on the job queue when to spin up new machines and when to tear down existing ones no longer needed.

Configuration happened under the runners.machine section in config.toml, where among other things IdleCount defined the number of permanently kept idle machines, and IdleTime determined how long an unused machine would remain available before being torn down. MaxBuilds additionally limited how many jobs a single machine could run before it was replaced with a fresh instance for security and cleanliness reasons.


# Excerpt from /etc/gitlab-runner/config.toml (Docker Machine, historical)
[[runners]]
  name = "autoscale-runner"
  executor = "docker+machine"
  [runners.machine]
    IdleCount = 2
    IdleTime = 600
    MaxBuilds = 20
    MachineDriver = "amazonec2"
    MachineName = "gitlab-runner-%s"
    MachineOptions = [
      "amazonec2-instance-type=t3.medium",
      "amazonec2-region=eu-central-1"
    ]

3. Why Docker Machine is no longer the future

Docker itself stopped further development of Docker Machine years ago, and the tool has since received no new cloud provider drivers and no security-related updates. GitLab kept relying on it for a long time regardless, since no direct replacement existed, but the lack of active maintenance gradually became a real operational risk, particularly against older, unmaintained cloud API versions that the existing drivers were still written against.

In response, GitLab built Fleeting, its own actively maintained plugin system intended to replace Docker Machine as the recommended autoscaling mechanism. For existing setups this does not mean Docker Machine suddenly stops working overnight, but it does mean new projects and migrations should be built on Fleeting rather than the foreseeably sunsetting Docker Machine path.

4. Fleeting: the new plugin-based autoscaling model

Fleeting abstracts provisioning and tearing down machines behind a clearly defined plugin interface, with separate plugins per cloud provider, such as fleeting-plugin-aws for AWS Auto Scaling Groups or fleeting-plugin-googlecompute for Google Cloud. Rather than attempting a monolithic abstraction across many providers the way Docker Machine did, Fleeting delegates the actual provider communication to dedicated, independently maintained plugins that stay closer to each cloud provider's own native autoscaling mechanisms.

Concretely, the AWS Fleeting plugin, for example, uses an actual AWS Auto Scaling Group as its backend, instead of creating every instance individually via API call the way Docker Machine did. This means many proven cloud-native mechanisms, such as spot instance support, lifecycle hooks, and health checks, come directly from the cloud provider itself instead of having to be reimplemented by the runner, which noticeably improves reliability over the older model.


# Excerpt from /etc/gitlab-runner/config.toml (Fleeting, current)
[[runners]]
  name = "fleeting-runner"
  executor = "docker-autoscaler"
  [runners.autoscaler]
    plugin = "fleeting-plugin-aws"
    capacity_per_instance = 1
    max_use_count = 20
    max_instances = 10
    [runners.autoscaler.plugin_config]
      name = "gitlab-runner-asg"
      profile = "default"
    [runners.autoscaler.connector_config]
      username = "ec2-user"
      use_external_addr = true
    [[runners.autoscaler.policy]]
      idle_count = 2
      idle_time = "10m"

5. From Docker Machine to Fleeting: what actually changes

The executor name changes from docker+machine to docker-autoscaler, and the previously central configuration in the runners.machine block moves into a new runners.autoscaler block with plugin-specific subsections. Anyone already running a working Docker Machine configuration cannot simply copy the old values one to one, since Fleeting plugins sometimes use different concepts, such as Auto Scaling Groups instead of individual instance creation, which also requires adjusting the underlying cloud infrastructure.

In practice a gradual migration is recommended: a new Fleeting-based runner is set up alongside the existing Docker Machine runner and initially targeted only at a subset of projects or via tags. After an observation period confirming reliability and cost behavior with the new model, the old Docker Machine runner can be phased out gradually, without requiring a hard cutover on a single day.

6. Cost control through the right autoscaling parameters

The most important cost levers are idle_count, idle_time, and max_instances. Too high an idle_count keeps more machines available at all times than necessary, producing unnecessary baseline cost, while too low a value means almost every job has to boot a fresh machine, which injects noticeable delay into every pipeline through instance boot time. The right value depends heavily on the typical pipeline pattern and should be tuned against real usage data, not gut feeling.

max_instances caps the absolute upper limit of simultaneously running machines and is the most important safeguard against cost blowouts, for example when a faulty trigger suddenly fires hundreds of pipelines at once. Without this cap, an autoscaler would in theory try to spin up a new machine for every waiting job, which with usage-based cloud billing can quickly turn into an unpleasant surprise on the next invoice.


# Cost-conscious autoscaling policy in config.toml
[runners.autoscaler]
  max_instances = 15
  max_use_count = 30
  [[runners.autoscaler.policy]]
    idle_count = 1
    idle_time = "5m"
    scale_factor = 0.0
    scale_factor_limit = 5

7. Spot instances and further cost optimizations

For non-time-critical jobs like lint checks or non-production test suites, spot instances are worth using: cloud providers offer them at significantly reduced prices, but can reclaim them at any time with short notice. Fleeting plugins like fleeting-plugin-aws support spot instances natively through the underlying Auto Scaling Group, which makes integration considerably simpler than manually managing spot instances on a Docker Machine basis.

It is also worth deliberately separating runners by job criticality: production deploy jobs run on regular, guaranteed on-demand instances via a dedicated runner with corresponding tags, while the bulk of build and test jobs are handled by a separate, spot-based runner. If a spot instance disappears mid-job, GitLab generally retries the job automatically on a new machine, which is an acceptable trade-off between cost and reliability for non-critical jobs.

8. Monitoring: making autoscaling behavior visible

Without monitoring, autoscaling remains a black-box mechanism where it is unclear whether the chosen idle_count and idle_time values actually fit, and whether unexpected cost spikes are occurring. GitLab Runner exposes Prometheus metrics, including counts of active, provisioning, and terminating machines, which can be visualized in a Grafana dashboard alongside the cloud provider's own cost metrics.

Sensible alerting reacts to two extreme situations: a persistently high job queue length, which points to overly restrictive max_instances values, and an unexpectedly high number of simultaneously running instances, which could indicate a faulty trigger or misuse. Together, both signals allow the autoscaling parameters to be iteratively refined instead of being set once and never touched again.

9. Docker Machine or Fleeting: the decision at a glance

For new runner setups there is hardly any good reason left to start with Docker Machine, since the tool is no longer actively developed and Fleeting now provides mature plugins for the common cloud providers. Existing Docker Machine runners do not necessarily need to be replaced immediately, but should be prepared for migration in the medium term before compatibility issues arise with newer cloud APIs or GitLab Runner versions.

The table below summarizes the key differences between both models and shows what to watch for when planning a migration.

Criterion Docker Machine Fleeting Consequence
Maintenance status Discontinued for years Actively maintained by GitLab Prefer Fleeting for new setups
Provider integration Generic API calls Native mechanisms (e.g. ASG) Better reliability with Fleeting
Spot instance support Manually bolted on Native via cloud backend Simpler cost optimization
Executor name docker+machine docker-autoscaler Migration requires config rework

Mironsoft

CI/CD pipelines, zero-downtime deployments and release automation

Deployments that run without downtime and without the nail-biting?

We review existing GitLab pipelines for fragile deployment steps and missing safeguards, then build a release process with zero-downtime deployments, automated checks and a rollback you can actually trust in an emergency.

Pipeline Review

Checking an existing .gitlab-ci.yml for fragility, missing stages and security gaps.

Zero-Downtime Deployment

Building symlink releases, health checks and rollback strategies for Magento stores.

CI/CD Automation

Connecting tests, security scans and deployments into one reliable pipeline.

10. Summary

Runner Autoscaling: Key Takeaways

Autoscaling goal

Spin up machines only when actually needed instead of permanently overprovisioning.

Docker Machine

Older, no longer actively maintained basis for autoscaling, still functional.

Fleeting

New plugin-based model with native cloud mechanisms like Auto Scaling Groups.

Cost control

Combine idle_count, idle_time, max_instances, and spot instances deliberately.

11. FAQ: Runner Autoscaling: Key Takeaways

1Does Docker Machine still work as a GitLab runner executor?
Technically yes, existing configurations keep running, but the underlying Docker Machine tool no longer receives active development, which is why GitLab recommends Fleeting for new setups.
2Do I need to migrate to Fleeting immediately?
Not necessarily immediately, but medium-term migration planning makes sense, since Docker Machine no longer receives new cloud API versions or security updates.
3What is the fundamental difference between the two models?
Docker Machine provisions instances individually via generic API calls, while Fleeting delegates provisioning to provider-specific plugins that use native cloud mechanisms like Auto Scaling Groups.
4Does Fleeting support every cloud provider that Docker Machine supported?
No, official plugins currently exist for the major cloud providers like AWS and Google Cloud, plugin availability may be limited for less common providers.
5How do I prevent autoscaling from blowing up the cloud bill?
The most important safeguard is max_instances as a hard cap, combined with monitoring of actually running instances and alerting on unusual spikes.
6What happens if a spot instance is reclaimed in the middle of a job?
GitLab detects the interruption and generally retries the job automatically on a new machine, which is acceptable for non-critical jobs like tests but should be avoided for production deploys.
7What exactly does idle_time do?
idle_time determines how long an unused machine is kept available after its last job before being automatically torn down. A higher value reduces wait time for the next job but increases cost.
8Can I run Docker Machine and Fleeting in parallel?
Yes, this is actually the recommended path for a gradual migration: a new Fleeting runner runs alongside the existing Docker Machine runner until it has proven itself.
9Does Fleeting require different cloud infrastructure than Docker Machine?
Partially yes, since some Fleeting plugins build on cloud-native concepts like Auto Scaling Groups that need to be set up separately in the cloud environment, whereas Docker Machine created instances individually and directly.
10Is autoscaling worth it for small teams with few pipelines?
With very low and constant pipeline volume the benefit is limited, but as soon as noticeable load spikes occur at certain times of day, the cost advantage usually outweighs the effort even for mid-sized teams.