Chapter 05Lesson 03~125 minutes

Runner Executors: Shell, Docker, Docker Autoscaler, Kubernetes, SSH, and Custom Execution Models: Configuration, Design Choices, and Tradeoffs

Executor choice is a platform architecture decision. This lesson compares host reuse, containerization, pod scheduling, autoscaled instances, remote SSH hosts, and custom provisioning by trust population, host access, image control, network and volume exposure, startup latency, elasticity, compatibility, observability, maintenance burden, and cost.

Design tradeoffsElasticityPlatform supportCostOperational ownership

Learning objectives

  • Choose Shell only when host-level simplicity is worth the reduced isolation and the workload/users are trusted.
  • Choose Docker when reproducible images and container isolation fit the workload without unsafe privilege or host-socket exposure.
  • Choose Kubernetes when an existing cluster and platform team can own pod scheduling, RBAC, networking, quotas, and runner integration safely.
  • Choose autoscaled ephemeral capacity when elasticity or cross-job residue reduction justifies cloud/Fleeting complexity and bounded cost.
  • Document why SSH or Custom execution is necessary before accepting their maintenance-mode status and bespoke failure surface.
Design principle: choose the narrowest execution boundary that satisfies the workload while containing untrusted code, making the environment reproducible, and keeping operations supportable. “We already run Docker/Kubernetes” is context—not a sufficient executor decision.

1. Start with workload and trust requirements, not executor names

Executor selection should begin with a short contract: who can modify the code; whether merge-request/fork code is untrusted; required OS/architecture/devices; whether a container image can represent the toolchain; expected job duration and concurrency; network destinations; secrets/identities; filesystem persistence; required service containers; startup latency; elasticity; and which team owns the underlying platform.

A GPU firmware test on a physically attached device may justify a tightly scoped trusted Shell runner. Ordinary Linux build/test workloads usually benefit from containerized execution. A large cloud-native organization may prefer Kubernetes because it already owns cluster policy and observability. Burst-heavy jobs that need strong cross-job destruction may favor ephemeral autoscaled instances.

2. Decision matrix: compare the boundary that actually matters

Criterion Shell Docker Docker Autoscaler Instance Kubernetes SSH / Custom
Isolation default Low; host process. Container boundary. Container + selectable ephemeral instance boundary. Instance/VM boundary; job has host access. Per-job pod boundary. Depends on remote/custom implementation.
Environment reproducibility Host-managed. Strong via image/tool pinning. Docker model plus worker image. VM image + provisioning/toolchain. Image + cluster policy/runtime. Often weaker/bespoke.
Elasticity Manual/static by default. Manual/static host capacity. Built for Fleeting autoscaling. Built for Fleeting autoscaling. Cluster scheduler + node capacity. External/bespoke.
Image/services support No job image/services. Yes. Yes, Docker-compatible. No job image semantics. Yes. Limited/custom.
Operational owner Runner host team. Runner + Docker host team. Runner manager + cloud/Fleeting team. Runner manager + cloud/Fleeting/VM image team. Runner + Kubernetes platform team. Remote/custom driver owner.
Current development status Maintenance mode. Active. Active. Active. Active. Maintenance mode.

3. Shell simplicity versus host risk

Shell minimizes executor layers and can expose local devices/tools directly. The cost is that the runner host becomes part of every job’s trust boundary. If multiple projects or untrusted contributors can schedule work, the host may become a cross-project data and credential boundary.

A defensible Shell design therefore uses narrow project scope, trusted refs/users, least-privileged runner account, network segmentation, no broad host credentials, deterministic workspace cleanup, dedicated hardware where possible, and an explicit reason containerized/ephemeral execution cannot meet the requirement.

4. Docker isolation versus daemon trust

Docker is often the best default for ordinary CI because the job image captures much of the userland, services are supported, and a container can be discarded after each job. The hard part is protecting the host daemon and mounts.

Non-privileged execution with minimal capabilities and no sensitive host mounts is substantially safer than privileged Docker. Mounting /var/run/docker.sock gives the job powerful control over the host Docker daemon; do not describe that pattern as “non-privileged and therefore safe.” If image builds require elevated capability, isolate them onto dedicated ephemeral workers and keep ordinary test jobs separate.

Executor separation pattern: a general non-privileged Docker runner for build/test jobs, plus a distinct protected runner for narrowly justified privileged/image-building work. Separate tags, scope, network, credentials, and worker lifecycle.

5. Kubernetes scalability versus cluster complexity

Kubernetes can offer high scheduling density, resource requests/limits, namespace controls, autoscaling integrations, pod-level observability, and familiar cloud-native primitives. But every benefit comes through cluster policy that someone must own: runner service-account RBAC, namespaces, pod security, node pools, host mounts, network policy, admission controls, quotas, image pulling, DNS, storage and cluster upgrades.

Do not place general untrusted CI pods next to sensitive production workloads merely because Kubernetes can schedule both. Separate trust domains through clusters/node pools/namespaces and policy appropriate to your threat model.

6. Static capacity versus ephemeral autoscaled workers

Static workers reduce startup latency and operational moving parts, but reuse creates residue and compromise-persistence questions. Ephemeral workers reduce cross-job residue when each worker is destroyed, but add provisioning latency, cloud dependencies, image lifecycle, quota/capacity failures, and cost-control requirements.

Docker Autoscaler and Instance make ephemeral policies first-class through Fleeting and task scaling. A high-isolation design can set one job per worker. That does not eliminate all risk: the manager, provider credentials, base image, network and artifact/cache services still need hardening.

7. Docker Autoscaler versus Instance executor

Question Docker Autoscaler Instance executor
What runs the job? Docker executor on autoscaled instance. Job runs against the autoscaled instance environment.
image: / services? Docker executor compatibility. No Docker-style job image semantics in the executor compatibility model.
Best fit Teams wanting Docker job behavior plus elastic/disposable VM capacity. Jobs needing full host/OS/device access plus elastic/disposable VM capacity.
Primary trust issue Container privilege/mounts plus worker/manager/provider trust. Job has host-level worker access; worker image, tenancy and destruction policy are critical.
Reproducibility anchor Worker image + Docker job image/digest. Worker VM image + provisioning/toolchain versions.

8. Maintenance-mode executors require lifecycle justification

Shell, SSH and Custom can remain correct for existing systems, but current GitLab documentation marks them maintenance mode. A new platform should avoid building strategic dependencies on a maintenance-mode executor unless an explicit requirement justifies it and an owner accepts migration/compatibility risk.

For SSH, document remote-host identity, credentials, patching, workspace cleanup, network trust and feature gaps. For Custom, version the driver, test its lifecycle against Runner upgrades, preserve driver logs, validate cleanup idempotency, and define what happens when prepare/run/cleanup phases fail independently.

9. Tier, offering, and platform constraints

The executor implementations themselves are available across GitLab Free, Premium, and Ultimate according to current executor documentation, but infrastructure is not free simply because the GitLab feature is. Docker hosts, Kubernetes clusters, VM autoscaling, cloud APIs, GPUs, storage and network egress have their own ownership/cost model.

GitLab.com-hosted runners are a service option distinct from operating your own self-managed executor fleet. On Self-Managed GitLab, your organization supplies runner capacity. Never hard-code current GitLab-hosted compute quotas or price assumptions into an architecture decision; verify current offering and billing documentation.

10. Worked choices: justify with evidence

Scenario Recommended starting point Why Required evidence before approval
Public-fork Linux unit tests Isolated non-privileged Docker or ephemeral Docker Autoscaler. Reproducible image + stronger separation from persistent host. No privileged/socket mounts, scoped network, clean worker lifecycle, untrusted-ref secret isolation.
Trusted FPGA lab with physical USB/JTAG hardware Dedicated project-scoped Shell runner may be justified. Direct device access may not map cleanly to containers. Trusted user/ref policy, dedicated host, least privilege, network isolation, deterministic cleanup.
Large Kubernetes-native microservice organization Kubernetes executor. Existing cluster/platform team can own pod scheduling/policy at scale. RBAC, namespace/node isolation, pod security, network policy, quotas, manager/cluster logs.
Burst-heavy image-based CI requiring one job per VM Docker Autoscaler with one-use workers. Docker job model plus elastic ephemeral host boundary. Dedicated autoscaling resource, max_use_count=1, bounded instances, provider IAM, worker destruction.
Legacy appliance reachable only by SSH SSH only with documented exception. External product constraint. Host identity, key scope/rotation, residue cleanup, feature gaps, migration plan.

11. Minimal executor decision record

executor_decision:
  workload: "trusted integration tests"
  code_trust: "protected branches; no fork code"
  required_platform: "linux/amd64"
  devices: "none"
  selected_executor: "docker"
  privilege: "non-privileged"
  sensitive_mounts: "none"
  network_scope: "package mirrors + GitLab only"
  worker_reuse: "host persistent; job containers disposable"
  image_identity: "human-readable release + verified digest"
  owner: "platform-ci"
  evidence_retention: "pipeline/job/runner/image logs for 30 days"
  residual_risk: "shared kernel and persistent runner host"
  reevaluate_when: "untrusted fork execution or privileged builds are introduced"

The value is not the exact schema; it is forcing the platform to state assumptions that are otherwise hidden inside config.toml, cloud consoles, cluster RBAC, and tribal knowledge.

Next lesson

Diagnostics, Failure Modes, Security, and Performance

Use the decision model to diagnose routing, image startup, privilege, socket, Kubernetes RBAC, autoscaler and teardown failures without weakening the boundary.

Knowledge check

When can Shell be a rational choice despite weaker isolation?

Why might Docker Autoscaler be safer than one persistent Docker host for hostile workloads?

Does Kubernetes automatically make CI secure because every job gets a pod?

What is the strongest reason to choose Instance over Docker Autoscaler?

Why should a new Custom executor adoption have an exception record?

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

  • GitLab Runner executors — current executor selection guidance, compatibility matrix, actively developed paths, and maintenance-mode executors.
  • Shell executor — host-local execution, shell selection, process termination, maintenance-mode status, and security warnings.
  • Docker executor — image/service model, container lifecycle, volumes, image pull behavior, and executor configuration.
  • Docker Autoscaler executor — Fleeting-based autoscaling, Docker feature compatibility, autoscaling resources, capacity and ephemeral-worker patterns.
  • Instance executor — Fleeting-based instance provisioning, full host access, worker images, capacity, and native-step considerations.
  • Kubernetes executor — per-job Pod creation, build/helper/service containers, RBAC requirements, entrypoint behavior, and executor configuration.
  • SSH executor and Custom executor — exceptional/maintenance-mode execution models and operational constraints.
  • Security for self-managed runners — Shell risk, Docker privilege/capability guidance, network segmentation, and trust-boundary recommendations.
  • Use Docker to build Docker images — security and executor implications for Docker-in-Docker and socket/pipe binding.
Verification note

Executor behavior and status were rechecked against current primary GitLab documentation on 2026-09-11. At verification time, Docker, Docker Autoscaler, Instance, and Kubernetes are actively developed executor paths; Shell, SSH, VirtualBox, Parallels, and Custom are maintenance mode, and Docker Machine is deprecated. Docker Autoscaler is documented as generally available since GitLab Runner 17.1 and uses Fleeting plugins. Re-check these facts before future course revisions.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.