GitLab Runner Docker Autoscaler on AWS

How to build a self-managed GitLab Runner fleet on AWS with the Docker Autoscaler executor, the AWS fleeting plugin, an Auto Scaling group and a shared S3 cache, and what to plan before you start.

On this page

How Docker Autoscaler on AWS scales GitLab CI jobs for your self-managed instance

The Docker Autoscaler executor, combined with the AWS fleeting plugin, lets a GitLab Runner manager start and stop cloud virtual machines in an AWS Auto Scaling group as CI demand changes, and then run each job in a Docker container on one of those machines. Teams choose this pattern when their self-managed GitLab queue grows unevenly, for example during release weeks or when several teams push at the same hour, and when a fixed set of runners either sits idle or leaves jobs waiting. The runner manager is the long-lived process that polls your GitLab instance for jobs, requests capacity from the fleet, and decides when instances are no longer needed. It should run on a host you control and keep running, because it holds the state that tells the fleet what to do next.

Each job is scheduled onto an instance that the Auto Scaling group provides, and Docker isolates the job's container from the host operating system and from other jobs on the same machine. Instances are created when queued jobs exceed available capacity and removed once they have been idle past the limits you configure, so the idle capacity you pay for is a planning decision rather than a default. Before you settle on sizing, confirm the current plugin settings, supported Auto Scaling group options and the Docker Autoscaler documentation in the GitLab Runner documentation, and keep the Runner and GitLab versions aligned, since the Runner documentation advises matching major and minor versions to your GitLab instance. Runner is MIT licensed, so the software cost is zero, and the recurring cost sits in the AWS instances, storage and network traffic your fleet consumes.

Which runner executor fits your CI workload on AWS

Docker Autoscaler suits pipelines whose jobs already run inside container images and do not need privileges beyond what a Docker daemon on the instance provides. The executor starts a fresh Docker environment on each autoscaled VM, which keeps job state isolated and lets the Auto Scaling group add or remove capacity as the queue changes. This is the default path for most application builds, test suites and image builds on AWS.

  • Job shape: jobs define an image and run steps inside containers, with no need to change kernel modules or host packages.
  • Isolation: each job runs in containers on a VM that is typically reused only within the autoscaler's policy, so confirm how long instances persist in your configuration.
  • Caching: fresh instances start empty, so pair the fleet with a shared S3 cache to avoid repeated dependency downloads.
  • Image pulls: the cost of pulling large base images on every new instance grows with image size, so measure pull times before sizing.
  • Privileged builds: container image builds that require privileged mode need a deliberate decision about trust and isolation, covered in your fleet design.

Check whether your heaviest jobs can run in standard containers and whether your team can accept the trust model of a shared Docker host.

Configuration choices for the AWS runner fleet

This table compares the configuration decisions for a Docker Autoscaler fleet on AWS, from manager placement to the shared S3 cache. Read each row as a question to settle before build, with the trade-off that drives the choice. Exact values for idle capacity, instance limits and cache paths should come from your own workload measurements and the current GitLab Runner documentation.

DecisionChoose it whenTrade-off to weigh
Manager instance placementThe manager should stay up and reachable to GitLab and the Auto Scaling group, so placing it in a stable subnet with a fixed role is the usual starting pointA dedicated on-demand instance costs more to run continuously, but a spot instance can be reclaimed mid-scheduling and stop job dispatch
Manager instance sizingJob volume and concurrency are high, or the manager also runs many autoscaled workers from one config.tomlA larger manager costs more every hour it runs; undersizing shows up as slow job pickup rather than obvious failure
Auto Scaling group capacity limitsYou need an upper bound on spend and a floor for fast starts, set from observed peak concurrencyA low maximum queues jobs during peaks; a high maximum raises the cost ceiling if pipelines loop or runaway jobs appear
Idle countTeams expect jobs to start in seconds, so a few warm instances are kept readyEach idle instance is paid for whether or not jobs run; zero idle saves cost but adds boot time to the first job
Idle timePipelines arrive in bursts with short gaps, so instances should linger briefly before terminationA longer idle time reuses warm machines but keeps paying for them; a short one scales down quickly and cold-starts more often
Docker images and pull policyImages are stable and pinned, so a pull policy that reuses local images cuts startup timeReusing cached images can serve stale layers; always pulling adds network time and registry load on every job
Shared S3 cache with Shared enabledJobs across many fresh instances should restore the same dependency cache from one bucketShared caches can be overwritten by unrelated branches or projects, so cache keys need careful naming
S3 cache with Path settingsCaches must be split by project, branch or runner group for isolation or cleanupNarrow paths reduce cross-job pollution but lower hit rates; confirm the cache key and path rules against the current runner docs

Setup sequence for the Docker Autoscaler fleet

This sequence takes an empty AWS account to a working autoscaling runner fleet, with each layer confirmed before the next one depends on it. The end result is a manager that provisions Docker hosts in an Auto Scaling group, a shared S3 cache, and test jobs that prove the whole path works.

  1. Prepare network and IAM roles

    Your cloud team creates the VPC subnets, security groups and the IAM roles that the runner manager and the worker instances will assume. The manager role needs permission to create and terminate instances in the Auto Scaling group, and the worker role needs only what the instances require. Confirm the exact permission set against the Runner autoscaling documentation for your plugin version before granting it. Done means you can launch a test instance into the target subnet with the worker role attached and reach the GitLab instance from it.

  2. Create the Auto Scaling group and launch template

    Build a launch template with an AMI that has a supported Docker engine, the settings the workers need, and the security group from the previous step. Create the Auto Scaling group from that template, with minimum and maximum capacity you set for your workload and no fixed instances at start. Verify that the group can scale out and in manually and that a launched instance appears with the expected tags. Record the group name and ARN, since the fleeting plugin configuration refers to them.

  3. Install the runner manager with a glrt token

    Create the runner in your self-managed GitLab with the scope you intend (instance, group or project), then copy the authentication token that begins with glrt-. Install GitLab Runner on a separate VM in a subnet that is not used for worker capacity, keeping it on-demand rather than spot. Register the runner with the token instead of the legacy registration token. Done means the runner appears as online in the GitLab interface and reports the version you installed, which should match your GitLab version as the Runner documentation advises.

  4. Configure config.toml for Docker Autoscaler

    Edit the runner's config.toml to set the Docker Autoscaler executor, the fleeting plugin for AWS, the Auto Scaling group reference, the Docker image defaults and the capacity limits for idle and maximum instances. Confirm the plugin release, the section names and each config key against the Runner documentation for your installed version, because these keys change between releases. Run the runner's configuration check and restart the service. Done means the runner starts without configuration errors and the log shows the plugin connecting to the Auto Scaling group.

  5. Connect the S3 distributed cache

    Create an S3 bucket for the shared cache in the same region as the workers, and grant the worker role read and write access to only that bucket. Add the cache block to config.toml with the bucket, region and authentication method, and set the Shared and Path options only if you need caches split or shared across runners. Confirm each cache key against the Runner documentation. Done means a job writes an object into the bucket and a later job on a different instance restores it.

  6. Run test jobs and confirm scaling

    Push a small pipeline that requests the runner's tags and runs a command that prints the instance hostname and Docker version. Trigger several jobs at once to confirm the group scales out, then wait for idle instances to scale in after the queue empties. Check the job logs, the Auto Scaling group activity history and the cache restore step. Done means jobs complete on fresh instances, the group returns to its idle capacity, and the cache is restored across instances.

  7. Record the configuration and confirm limits

    Store the config.toml, launch template version and IAM policy documents in your version control system, with secrets kept in your secrets manager rather than in the file. Ask your team to confirm the capacity limits, the plugin support status and the upgrade path with the vendor documentation before production use. Done means another engineer can rebuild the fleet from the recorded artifacts and the open questions are assigned to an owner.

Securing the runner manager, workers and cache

A Docker Autoscaler fleet on AWS has four places where a weak control becomes a path into your build environment: the manager, the cloud identity it uses, the workers that run jobs, and the shared cache. The cards below take each one in turn, with the risk, the control and what to check before the fleet takes production jobs.

Keep the manager off spot capacity

The runner manager holds the registration token and the connection to GitLab, and it creates and removes worker instances. Place it on an on-demand instance in a fixed Auto Scaling group, not on a spot instance that AWS can reclaim mid-scale. A reclaimed manager stops scheduling jobs and can leave orphaned workers running. Check that the manager's group is separate from the worker group and that its replacement behavior is tested.

Scope the fleeting plugin's IAM role

The fleeting plugin needs permission to launch, describe, tag and terminate instances in the worker Auto Scaling group, and nothing beyond that. Grant those actions on the specific group ARN, deny broad EC2 administration, and attach the role to the manager instance profile rather than long-lived access keys. Review the policy whenever the plugin version changes, because required actions can change between releases.

Separate untrusted projects from privileged jobs

Jobs from forks, external contributors or unreviewed branches should never run on workers that can reach deployment credentials or the cache bucket with write access. Use distinct runner tags and separate Auto Scaler groups or launch templates for trusted and untrusted work. Confirm that protected-branch and protected-variable rules keep production secrets away from the untrusted pool.

Restrict cache bucket access

The shared cache in S3 holds dependencies that later jobs will restore and run, so a writable bucket lets one job poison the cache for others. Give worker roles read and write access only to their own cache prefix, deny public access on the bucket, enforce encryption in transit, and enable versioning so a bad object can be rolled back. Ask whether jobs on untrusted runners should use a separate bucket or prefix.

Patch worker images on a schedule

Autoscaled workers are rebuilt from an image, so a patch only reaches jobs once the image changes and instances are replaced. Build worker AMIs from a pinned base, rebuild them on a set cadence and after vendor advisories, and roll the group to the new image. Keep the GitLab Runner version aligned with the GitLab instance, since the Runner documentation advises matching major and minor versions.

Protect the registration token

A runner registration token can register new runners against your instance, so treat it as a credential. Keep it in AWS Secrets Manager or Systems Manager Parameter Store with restricted read access, not in user data, launch templates or CI variables visible to projects. Plan to move to authentication tokens and rotate any legacy token once the fleet is stable, and confirm that old tokens are revoked.

How idle capacity and cache choices change your AWS bill

The largest recurring cost in an autoscaling runner fleet is capacity you pay for while nothing is running jobs, so the first decision is how many instances the Auto Scaling group keeps warm. In the Docker Autoscaler setup, the idle settings in the runner's config.toml control how many machines stay ready for incoming jobs and how long an instance sits unused before the fleet removes it. A higher idle count shortens queue waits because jobs start on machines that already exist, but every warm instance bills by the hour whether or not a pipeline runs. A lower idle count and a shorter idle timeout cut that spend and increase the chance that a burst of pipelines waits for new instances to boot. Ask your AWS team which instance family and purchasing option (on-demand, savings plans or spot) the Auto Scaling group uses, because those choices change the hourly rate more than any runner setting. Confirm the exact setting names and their behavior against the version of GitLab Runner you have installed in the GitLab Runner documentation.

The shared cache in S3 is the second lever, and it works against idle spend. A fresh instance has no local dependency cache, so a job that restores packages from the bucket spends time downloading them, and that time is billed as instance runtime. Short jobs on cold machines can spend a large share of their duration on cache misses, which pushes up instance-hours even when the fleet is small. Caching more aggressively reduces that waste but adds recurring S3 storage, request charges and data transfer, especially if runners sit in a different region from the bucket or if the cache holds large build artifacts. Set cache retention and expiry rules on the bucket, and review which cache keys are shared across branches, because unbounded keys grow the stored data every month. Recurring AWS costs therefore fall into three groups: instance-hours for warm and busy capacity, storage and requests for the cache, and data transfer. The one-time cost is the build effort to write the Auto Scaling group, IAM roles and tuning, which a partner quote should price separately from these running charges.

Limits to confirm in your AWS account and GitLab version

A Docker Autoscaler fleet depends on four moving parts: the Runner, the fleeting plugin, the GitLab release and your AWS account. Each has limits that change over time, so confirm them against your own versions and quotas before you commit to a design.

Fleeting plugin version compatibility

Confirm which version of the AWS fleeting plugin matches your installed GitLab Runner and your GitLab release. The plugin is versioned separately from the Runner, so a Runner upgrade can leave the plugin behind. Ask whether the plugin release you plan to use is supported with the Runner major and minor version you intend to run, and record that pairing in your change records. Use the pairing check before every Runner upgrade.

AWS service quotas for instances

Check the EC2 service quotas for the instance families and purchase options your fleet will request, in the regions and accounts you plan to use. Autoscaling fails quietly when a quota is reached, so jobs queue without an obvious error. Ask your AWS account team or review the Service Quotas console for on-demand vCPU limits, and confirm whether the quota covers the peak number of concurrent jobs you expect, not just the average.

Runner alignment with upgrade stops

Confirm how your planned GitLab upgrade path affects the Runner version you run. The Runner documentation advises keeping major and minor versions aligned with GitLab, and GitLab requires upgrades to pass through required upgrade stops. Ask whether a required stop falls inside your upgrade window, and whether the Runner must move to a specific release at that stop before or after the GitLab server changes.

Docker Machine migration scope

Determine whether any existing fleet still uses the Docker Machine executor. If it does, confirm the removal timing for that executor in the GitLab release you target and whether your fleet must move before that release. Ask whether your current config.toml settings map cleanly to Docker Autoscaler, and whether a parallel cutover by runner tags is feasible without idle capacity doubling your cost.

Instance and region availability

Confirm that the instance types, AMIs and availability zones you plan to use exist in your target region and are approved by your security team. Ask whether the Auto Scaling group can span the zones you need, and whether your organization restricts the AMIs or regions that workloads may run in. These limits shape both the idle capacity you can keep and how quickly the fleet recovers from a zone issue.

Shared cache bucket permissions

Confirm that the S3 bucket for the shared cache, the IAM role attached to the runner manager and the autoscaled instances, and any bucket encryption or lifecycle rules all match your policy. Ask whether your organization requires a specific key management setup for objects written by CI jobs, and whether the bucket's region matches the fleet to avoid cross-region transfer surprises.

Questions teams ask before building an autoscaling runner fleet on AWS

How much does it cost to build an autoscaling runner fleet on AWS?

The build cost has two parts: the one-time engineering work to design, configure and test the fleet, and the recurring AWS spend on the manager, autoscaled instances, load balancing and storage. GitLab Runner itself is MIT-licensed, so the software carries no license fee. Your AWS bill depends on instance sizes, how many jobs run at once, how long idle capacity is kept, and the size of the S3 cache. Ask any partner for a quote that separates engineering effort, AWS resources you will own, and ongoing support, and that states the assumptions behind the instance counts and idle settings.

How long does it take an experienced team to build this?

The duration depends mainly on how many runner types you need, how many AWS accounts and networks are involved, whether an Auto Scaling group and image pipeline already exist, and how quickly approvals come through for IAM roles and network changes. An experienced team that already runs GitLab and AWS will usually spend most of the time on networking, IAM and testing across job types rather than on the Runner configuration itself. Ask for a timeline tied to a written scope with named milestones, including a test pass on a non-production project before cutover.

What does the work need from our side?

Expect to provide an AWS account or accounts with permission to create VPC, IAM, Auto Scaling, EC2 and S3 resources, network paths from the runner manager to your GitLab instance and to the AWS APIs, and a GitLab account with administrator rights so runners can be registered. You also need a non-production test project that exercises realistic jobs, such as a Docker build and a dependency restore. Agree in advance who approves security group rules, IAM policies and the choice of AMI, because those approvals often set the pace.

Can our in-house platform team build this without a partner?

Yes, if the team has working knowledge of GitLab Runner, Docker, AWS networking and IAM, and time to test failure cases. The hard parts are rarely the first successful job. They are scale-in behavior under load, cache correctness across fresh instances, credential scope, and upgrading the manager and fleet in step with GitLab. A partner is most useful when the team needs a fixed scope, a documented handover, or someone to run the cutover. Decide based on the skills you already have and the number of people who will own the fleet after go-live.

What drives the quote up or down?

The largest drivers are the number of runner pools and workload types, the number of AWS accounts and regions, the existing state of your VPC, IAM and Auto Scaling setup, the need to keep parallel runners during cutover, and the amount of testing against real pipelines. Ask the quote to list each runner pool, its executor settings, the cache design, the monitoring and alerting included, the handover documentation, and what happens if a requirement changes during the build. Scope that is written down before work starts keeps change requests visible.

Can we migrate from Docker Machine runners to Docker Autoscaler?

Yes. Docker Machine is deprecated and is planned for removal in GitLab 20.0, so existing fleets should move before then. The usual approach is to map the settings in your config.toml to the Docker Autoscaler and AWS fleeting plugin settings, then run the new runners alongside the old ones and move jobs by runner tags. Plan the cutover by project group rather than all at once, and keep the old fleet available until the new one has passed your test jobs. Confirm the exact removal timing against GitLab's documentation for your release.

How does the S3 cache work with autoscaled instances?

Autoscaled instances are fresh, so a local cache disappears when an instance is removed. A shared cache in an S3-compatible bucket lets jobs on any instance restore dependencies such as package caches. The key decisions are the bucket layout, which IAM role the instances use to read and write, how long objects are kept, and whether caches are shared across runners or split by runner or project. Write access should be limited to the cache prefix, and you should test that a restored cache produces the same build result as a cold start.

What a DeployOpen fixed-scope runner engagement covers

A DeployOpen runner engagement starts with scope, not hours. Before work begins, we agree in writing which GitLab instance the fleet will serve, which AWS accounts, regions and VPCs will host the runner manager and the Auto Scaling group, which job types need Docker Autoscaler capacity, and which workloads stay on other runner types such as the Kubernetes executor. The scope also records the expected maximum concurrency, the instance types and AMI approach, the idle capacity policy, and whether the shared cache in S3 is in or out. Engagements are fixed-scope and quoted from that written scope, so a change to concurrency, regions or runner tags is a change request with its own effect on the quote.

The written deliverables for this topic are an architecture document, the configuration set, test evidence and a runbook. The architecture covers manager placement, network paths to GitLab and to the fleet, IAM roles for the fleet and the cache bucket, and the scale-out and scale-in behavior. The configuration set includes the runner config.toml, the Docker Autoscaler and fleeting plugin settings, the Auto Scaling group definition and the S3 cache settings. Test evidence shows jobs scaling out, idle instances scaling in, and a cache hit on a fresh instance. The runbook covers upgrades that keep the Runner version aligned with GitLab, rotation of credentials, and what to check when the fleet stops picking up jobs.

Your team provides the things only you can supply: access to the GitLab instance and an administrator to register runners, the AWS accounts and the IAM permissions needed to create the resources, network approvals, and a named owner who will review the design and accept the tests. You should also confirm your own change process and whether the runbook must follow an internal template. Ask any partner for the exact list of exclusions, the acceptance criteria and the support terms in the quote, and check that they match the written scope. DeployOpen does not publish a price list, and any retainer for ongoing upgrades or testing is scoped separately.

How DeployOpen scopes and delivers your runner fleet

DeployOpen plans, builds, documents and supports self-hosted platforms inside your own AWS accounts. For GitLab Runner Docker Autoscaler on AWS, we start from a written scope covering the manager, the Auto Scaling group, the shared cache and the operating runbooks, then deliver it in a sequence your platform team can review and run.

How we scope

  • Confirm the AWS accounts, regions, VPC layout and network paths the runner manager and worker instances will use
  • Record expected job concurrency, peak hours, job image sizes and the teams or projects that will share the fleet
  • Agree in writing which runner tags, scaling limits and cache settings are in and out of the fixed scope
  • Identify the GitLab instance version and confirm the Runner version will stay aligned with it

How we build

  • Place the runner manager on a dedicated instance that is not a spot instance and restrict its IAM permissions to what the fleet needs
  • Configure the Docker Autoscaler executor with the AWS fleeting plugin against an Auto Scaling group with planned idle capacity
  • Set up the shared distributed cache in S3 with access limited to the runner role and tested restore behavior on fresh instances
  • Test scale-out, scale-in and job failure handling in a non-production environment before cutover

How we hand over

  • Deliver infrastructure as code, runner configuration and a parameter record that your team can review and version
  • Document runbooks for adding runner tags, rotating runner tokens, upgrading the Runner and recovering the manager
  • Walk your engineers through monitoring of scaling events, queue times and cache hit behavior before sign-off
  • Provide the test evidence and configuration baseline that your security and change management reviews will need

How we support

  • Support is available as a retainer for Runner upgrades, GitLab upgrade stops and changes to scaling or cache settings
  • Scope any post-handover changes in writing, with a separate quote for new executors, regions or teams
  • Ask whether a retainer covers the Runner version alignment steps that accompany each GitLab upgrade
  • Confirm response expectations and escalation paths in the retainer agreement before the engagement starts

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.