Shell vs Docker Executors in GitLab Runner: Which Is Right for Your CI/CD?
Choosing the right GitLab Runner executor—shell or Docker—determines how jobs interact with the host, affecting speed, isolation, security, and reproducibility, and this guide compares their core features, use cases, risks, deployment options, and best‑practice recommendations for robust CI/CD pipelines.
Overview
GitLab Runner connects a GitLab repository with CI/CD jobs. The executor you choose determines how each job is run. The two most common executors are shell and docker, each with a distinct trade‑off between speed, isolation, and reproducibility.
Shell Executor
The shell executor runs a job directly on the host where the Runner is installed, using the gitlab-runner system user. All commands are executed in the host’s native environment.
Key characteristics
Shared environment All jobs share the same host filesystem, installed packages and environment variables. Changes made by one job can affect subsequent jobs.
Direct hardware access Jobs can read/write any host file, use network interfaces, and invoke any binary that exists on the machine.
Low overhead No container creation or teardown, so simple scripts start almost instantly.
Typical use cases
Deployments that need to copy binaries to system directories, restart services, or run database migration scripts.
Any task that must interact with host‑level resources (e.g., mounting devices, accessing privileged ports).
Potential risks
Environment pollution Different projects may require conflicting versions of tools or libraries, leading to a “dependency hell” on the host.
Security exposure Job scripts run with the same privileges as the gitlab-runner user; a compromised script can modify or delete host files.
Poor reproducibility Build results depend on the exact state of the host, making migration to new machines or scaling difficult.
Docker Executor
The docker executor creates a fresh Docker container for each job. All job commands run inside this container, which is destroyed when the job finishes.
Key characteristics
Environment isolation Each job runs in its own sandbox; dependencies of one project never interfere with another.
Environment‑as‑code The build image is defined in .gitlab-ci.yml via the image keyword (e.g., image: node:18‑alpine ). This guarantees consistent tooling across runs.
Enhanced security Jobs are confined to the container and have limited access to the host kernel and filesystem.
Typical use cases
Compilation, unit testing, integration testing, packaging, and any CI step that benefits from a clean, repeatable environment.
Runner Deployment Options for Docker Jobs
When using the Docker executor, the Runner itself can be deployed in two ways.
Runner on host (recommended) Install the Runner binary directly on the host OS. The Runner communicates with the host Docker daemon through /var/run/docker.sock to create, start, and remove job containers. This approach is simple, widely used, and avoids the extra layer of containerizing the Runner.
Runner inside a container (advanced) Run the Runner as a Docker container. To launch job containers, the Runner container must reach a Docker daemon, which can be achieved by:
Docker‑in‑Docker (DinD) Start a separate Docker daemon inside the Runner container. This adds nesting complexity and may impact performance.
Socket mounting Mount the host’s /var/run/docker.sock into the Runner container. The Runner then controls the host Docker daemon directly, offering better performance but granting the container extensive host privileges.
Best‑Practice Recommendations
Neither executor is universally superior; choose based on the job’s requirements.
Prefer docker executor Use it as the default for most CI tasks (build, test, package) because it provides reproducible environments and strong isolation.
Use shell executor when host interaction is required Deploy dedicated Runners on production or pre‑release machines for deployment scripts, service restarts, or any operation that must run against the host OS. Apply strict security controls and limit the runner’s scope.
A common hybrid strategy tags Runners with their executor type: docker -tagged Runners on development and test servers handle the bulk of CI work, while shell -tagged Runners with environment tags such as production or deploy run on production hosts for deployment steps.
Ops Development & AI Practice
DevSecOps engineer sharing experiences and insights on AI, Web3, and Claude code development. Aims to help solve technical challenges, improve development efficiency, and grow through community interaction. Feel free to comment and discuss.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
