Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Run CI jobs in a container chosen by the runner

7da9111ecabb236d48fc48592cd3d1d45f9afe78 · Keenan Nemetz · 15d ago · parent 474a812d

Run CI jobs in a container chosen by the runner

Each lease gets a disposable checkout and, when --image is set, its own container from that image. The runner keeps the SSH key and trusted Git cache outside the job, so workflow code cannot reach either; older caches are rebuilt because a previous job may have written to them.

The record names the image and runtime, and the queue mirrors them for the run page. Without --image the runner still executes on the host and says so.

docs/ci.md +50

@@ -109,6 +109,56 @@ vole runner --remote ssh://git@host:2222 --repos demo --repos docs
109 109 access to a repository.
110 110 - `--dir` chooses where checkouts are kept.
111 111
112 ### Run each job in a container
113
114 A runner can execute every job's steps in a container built from an image you
115 choose. The image belongs to the machine, not to the workflow: the workflow still
116 asks only for labels, and the operator decides what those labels mean.
117
118 ```sh
119 vole runner --remote ssh://git@host:2222 \
120 --image golang:1.27-bookworm@sha256:... \
121 --container-runtime podman \
122 --label linux --label go1.27
123 ```
124
125 One container is created per job and removed afterward. The workflow's steps run
126 inside it, but the runner process stays on the host: the SSH key, the trusted Git
127 cache, and the record push are never exposed to the job. Each job gets a fresh
128 clone, so a job cannot leave a Git hook or a modified checkout behind for the next
129 one.
130
131 The image is the dependency manifest. It should contain the toolchains and system
132 packages the builds need, plus `/bin/sh` and a POSIX `sleep` (Vole runs shell steps
133 and keeps the container alive between them). Pin it by digest; a mutable tag under
134 the same label silently changes what a build means.
135
136 Container defaults are intentionally closed:
137
138 - no network (`--container-network none`); add `bridge` only when a build needs it;
139 - read-only root filesystem, with `/tmp` as a writable tmpfs;
140 - no capabilities, `no-new-privileges`, and no host mounts except the job tree;
141 - no Docker socket, SSH agent, or host environment;
142 - the steps run as the runner user's `uid:gid` by default.
143
144 Use `--container-runtime` to choose `docker`, `podman`, or `nerdctl`. The runner
145 user must be able to talk to that runtime: rootless Podman is preferred, because
146 membership in the Docker group is effectively root on the host. Do not mount the
147 host's Docker socket into a job; that would undo the isolation.
148
149 `--container-pull` controls whether an image is pulled (`missing`, `always`, or
150 `never`). `--container-user`, `--container-userns`, `--container-memory`,
151 `--container-cpus`, and `--container-pids` override the user, namespace mode, and
152 resource limits. Rootless Podman uses `keep-id` by default so the numeric user can
153 write the bind-mounted job tree; for rootless Docker, choose a user that matches
154 the runtime's mapping (container root is the runner user) or use a runtime with
155 `keep-id`.
156
157 Without `--image`, jobs run directly on the runner host, as they did before. That
158 is useful for a trusted machine, but it is not a sandbox. The run record names the
159 image and runtime when a container was used, so the run page says what actually
160 executed.
161
112 162 All runners share one pool. An idle runner waits for work instead of asking
113 163 repeatedly, and Vole gives the next job to the repository with the least work in
114 164 progress. Set `max_jobs_per_repo` when you need a hard limit per repository.