GitLabOps Runners: Shell, Docker, Kubernetes Compared

GitLabOps Runners: Shell, Docker, Kubernetes Compared

Cardiology rarely treats before it classifies. Functional class is assigned, sounds are graded, and only then is a therapy chosen — because the same complaint calls for different interventions at different severities. Delivery engineering benefits from the same instinct. GitLabOps — the Atlas’s term for operating software delivery on GitLab — faces one of its earliest and most consequential classification choices in the executor: the mode each runner uses to isolate the work it accepts. Three modes cover nearly every real deployment, and choosing among them is less about fashion than about matching characteristics to workload.

GitLabOps Starts With Classifying the Workload

Before comparing machines, classify what will run on them. Four properties of a workload decide almost everything: where its code originates (first-party only, or contributions from outside the team), how much isolation a compromise would demand, how spiky its demand is across a day, and how visible its cost needs to be. A nightly internal report and a public open-source project’s merge-request stream look identical on a diagram and could not be more different in trust terms. Write the classification down per project before reading the comparisons below; most executor disputes dissolve once the classifications are on the table.

Shell: Direct Contact, Minimal Barrier

The shell executor runs each job directly on the runner host’s own environment, with the credentials and history of that host within reach. Setup is nearly instant and performance carries no container overhead, which explains its enduring popularity for quick internal chores. Its weakness is structural rather than tunable: work on a shared shell can observe what previous work left behind — files, caches, environment entries — and a malicious job can alter the machine for everyone after it. The professional consensus is therefore narrow and firm: reserve shell for fully trusted, first-party maintenance, and never point it at code arriving from outside the team. As a comparison dimension, shell scores highest on setup speed and lowest on isolation, with nothing in between. It suits the chores every server accumulates — pruning caches, rotating a local credential, checking disk headroom — precisely because those chores are written by the same people who own the machine.

Docker: Containment Without Commitment

The container executor starts each job inside a fresh image on a host the administrator controls. Every run begins from a declared state, dependencies are pinned in the image rather than accumulated on the machine, and one job’s mess is discarded with its container. For most web teams this is the sensible default: strong enough isolation for mixed internal workloads, modest operational burden, and predictable behavior across the fleet. Two qualifications keep the picture honest. A host that persists between jobs accumulates trust it has not earned unless configured to retire machines after a single assignment; and containers share the host kernel, so they confine mistakes rather than guaranteeing separation from determined attackers. Containment, not containment alone. Teams adopting this mode should also adopt image hygiene: one readable file describing how job images are built, versions pinned rather than floating, and a review whenever a base image gains privileges. None of that is exotic; it is the same record-keeping discipline that separates a reproducible pipeline from a lucky one.

Kubernetes: Elastic, With Fine Print

The Kubernetes executor schedules each job as a pod on a cluster, drawing capacity up and down with demand. For organizations already operating clusters, it consolidates scheduling, quotas, and monitoring into systems the team staffs anyway, and burst-heavy workloads fit it naturally. The fine print is financial and operational. Elastic capacity is not automatically economical — idle cluster overhead, cross-zone traffic, and per-pod startup latency all appear on the invoice in forms that are easy to underestimate, and the assumption that this executor is always the cheaper option does not survive contact with a real bill. It also presupposes cluster fluency; a team without one is signing up to build and operate a platform to run its tests. Watch two operational numbers once this mode is live: how saturated the cluster actually runs, and how long jobs wait before a pod is scheduled. Both drift quietly, and both surface first as complaints about slowness rather than as alerts from the cluster itself.

GitLabOps Judgment: Matching Executor to Workload

With the three profiles in view, five questions resolve most choices. One mechanism ties them together: labels. Every runner declares the kinds of work it will accept, every job states what it requires, and the platform’s scheduler performs the matching — so a fleet’s capabilities are ultimately written in its labels, and the five questions below are really questions about what those labels should say:

  1. Does any code from outside the team touch this workload? If yes, shell is eliminated before the discussion starts.
  2. Does the organization already run a Kubernetes cluster with people who understand it? If yes, pod-based execution inherits that investment; if no, the cluster becomes the project.
  3. Is demand bursty or steady? Bursts favor elastic execution; steady load favors a small, well-understood set of container hosts.
  4. Must cost be attributable per team or per project? Attribution is easier where capacity is explicit than where it is pooled.
  5. How quickly must a compromised job be assumed isolated? The answer ranks the three modes cleanly: fresh pod, fresh container, shared shell.

When the Scoreboard Flips Under You

Executor choice is a function of constraints, and constraints change. One documented case makes the point with numbers: an autoscaling container arrangement scored 3.85 against hosted machines at 4.30 — until the requirement for private-network reachability was found to be imaginary, the scoring was repeated, and the ranking inverted. Nothing about either technology changed; a premise did. A disciplined operation re-scores when a premise dies, and treats an executor decision as a standing hypothesis rather than a settled verdict.

A Closing Word on Classification

Cardiology’s classification systems exist because they change what happens next, as any reader of this overview of NYHA Class IV and its clinical context will recognize — severity assigned first, therapy second. The same logic runs through functional class NYHA as a framework, and through grading scales like this one on grading heart sounds and murmurs: sort first, act second, and record the sort so the next person can audit it. Executors deserve no less. For the complete treatment of runner fleets — trust tiers, tagging conventions, and the economics of each mode — the Atlas’s entry on GitLabOps is the natural next reference.