This article explains whether a pipeline job should run in a container or directly on the build machine. After reading it, you can write runner.mode in the YAML: DOCKER starts a container from the job’s image and runs there, NATIVE runs directly on the machine where the runner lives, and you can match the labels to the mode chosen when the runner was connected so the job lands in the environment you wanted.
A runner is the program installed on a build machine. It picks up jobs for the platform and runs them. A container is a short-lived isolated environment started from the job’s image. The host is the machine where the runner process itself is running. In the YAML, a task is a job: one group of steps that run in order. Pipelines and runners are available on Team and Enterprise. Community has no CI/CD menu, and the steps in this article do not apply there.
If you do not yet have a runner of the mode you need, sign in to AngusGit, open CI/CD → Runners in the sidebar, and click Add Runner. Change Control plane URL to an address the build machine can reach, name it for example build-linux-01, and click Generate token (it lasts 1 hour and is shown only once). For DOCKER, open the Run with Docker tab and set the labels to linux,docker. For NATIVE, open the Zip install tab, leave docker out of the labels, and install JDK 21 on the machine first. Copy the command onto the build machine and run it. Write the YAML after the list shows Idle. Changing the mode is not a label edit. Connect another runner from this same entry, using the other tab.
1. How the two ways differ
| DOCKER | NATIVE | |
|---|---|---|
| Where the command runs | In the container the runner starts for this job | On the build machine, in the environment of the runner process |
| Where JDK, Node.js, and other tools come from | The image named by image in the YAML |
Tools already installed on the build machine |
| Working directory |
/workspace inside the container |
data/workspaces under the runner install directory |
| Whether each run starts the same | Yes. Every job starts from the same image | It depends on the software and global state already on the machine |
| What to prepare on the build machine | Docker installed | Every build tool you need, and JDK 21 |
| How you change a tool version | Change image in the YAML |
Upgrade each build machine |
In both ways, the working directory is cleaned up when the job ends. The next job does not inherit it.
The current instance makes this easier to see. Open CI/CD → Runners. Two runners are already there: priv-1 is DOCKER, with labels linux and docker; dev-1 is NATIVE, with labels linux, native, and dev. Both executors are SHELL, and both show concurrency 0/2.
2. How to write it in YAML
A DOCKER job
pipeline:
build:
runner:
mode: DOCKER
image: repo.anguskit.com/docker-public/angus-ci-maven:21
labels: ["linux", "docker"]
steps:
- name: Build
run: mvn -B -ntp clean package -DskipTests
When mode: DOCKER, write image. Otherwise validation warns. The image needs every command this job uses, such as mvn above.
A NATIVE job
pipeline:
sign:
runner:
mode: NATIVE
labels: ["linux", "codesign"]
steps:
- name: Sign
run: ./scripts/sign-release.sh
A NATIVE job does not write image. Keep docker out of labels, and add a label that identifies this kind of machine, such as codesign above: the machine that has the signing certificate.
mode is the uppercase word DOCKER or NATIVE. A lowercase value fails validation.
What happens when mode is omitted
When mode is omitted, AngusGit does not filter runners by mode. Any runner whose labels match may take the job. The job also stays out of a container and runs directly in the runner’s environment. The same YAML on different machines can produce different results, so write mode on every job.
3. How the runner is set up
After the job’s mode is set, a runner of that mode has to be there to take it. A runner is one mode, and the mode is fixed the first time it is connected. A runner whose labels include docker is a DOCKER runner. A runner without that label is a NATIVE runner.
| Runner you want | How to connect it | Labels |
|---|---|---|
| DOCKER | Run with Docker or One-line install script | Default linux,docker. You can add more |
| NATIVE | Zip install, with JDK 21 on the machine | Must not include docker, for example linux,codesign
|
A runner connected with Docker itself runs in a container. It uses the mounted /var/run/docker.sock to start a new container on the host’s Docker for each job.
After it is connected, adding or removing the docker label in Edit runner changes only the label. The mode stays as it was. To change the mode, connect again from the start of this article, using the other tab.
4. How to choose
With no special constraint, use DOCKER. The build environment is in the YAML, so anyone reading it can see which JDK this build used. Changing the version is one line, and the build machines stay as they are. A new build machine is ready once Docker is installed and a runner is connected.
Use NATIVE in these cases:
- The build needs something that exists only on that machine, such as a signing certificate, a hardware key, a GPU, or a Windows or macOS toolchain.
- The build tool cannot be put into an image, or putting it in an image is impractical.
- The machine cannot have Docker installed.
The two modes can be mixed. In one pipeline, compile and test with DOCKER, and sign at the end with NATIVE. Each job writes its own runner. AngusGit picks a runner per job, as long as an online runner exists for each mode.
5. Extra care with NATIVE
A NATIVE job shares the build machine’s environment. Agree on these points ahead of time:
- Keep tool versions the same. JDK and Node.js versions on the NATIVE build machines should match. Otherwise the same code can build differently on different machines.
-
Keep build scripts from changing the global environment. For example,
npm install -gor editing~/.m2/settings.xmlaffects every later job on that machine. - Keep concurrency modest. Several NATIVE jobs on one machine compete for the same global cache and the same ports. Keep Max concurrency from going too high.
Common questions
Where does a DOCKER job pull its image?
From Docker on the host where the runner is running. If the build machine cannot reach the image address, the pull fails. On a private network, push the images you use to an internal registry and write that address in the YAML.
Can one machine offer both modes?
Yes. Install two runners on it: one connected with Docker, and one connected with Zip and without the docker label. Give them different names, for example build-01-docker and build-01-native.
Can runners be added automatically when the load is high?
That is not available today. Runners are connected one by one. When you need more capacity, connect another runner by hand, or raise Max concurrency on a runner you already have.
The job stays Pending, and the reason says the execution mode does not match. What now?
No online runner has the same mode as this job. Open this run. The wait reason beside the job is “No Runner matching mode DOCKER” or “No Runner matching mode NATIVE”. Connect another runner from the start of this article, using the matching tab, and look at this run again after the list shows Idle.
Summary
An AngusGit pipeline job either runs in a Docker container the runner starts, or it runs directly on the build machine. With DOCKER, the tools come from image, and every run starts the same. That is the usual choice. With NATIVE, the job uses tools installed on the build machine, which fits a job that needs something only that machine has. Write runner.mode and labels on every job. A DOCKER job goes to a runner that has the docker label. A NATIVE job goes to a runner connected with Zip and without the docker label. The runner’s mode is fixed when it is connected. Changing labels later does not change the mode.
Conclusion: For an ordinary build, use
runner.mode: DOCKERwith an image so the environment stays the same, and let a runner with thedockerlabel run it. When the job needs hardware or a certificate that exists only on that machine, userunner.mode: NATIVE, and let a Zip runner without thedockerlabel run it.



Top comments (0)