Iteration 2 of the throwaway spike. One file, still workflow_dispatch only, still deleted when #237 is answered.
What the first run told us
NO BUILDX
--- socket ---
srw-rw---- 1 root root 0 /var/run/docker.sock
GITEA_TOKEN is present
docker: command not found ← exit 127
Question 1 answered: no, the runner cannot build images as configured. The job container has no docker binary, so login failed and the three steps that mattered were skipped — no answer on whether the registry accepts a push, and no build time.
Two things it did settle, both useful:
/var/run/docker.sock is mounted, so the daemon is reachable and only the client is missing. That is closable from inside a step, without reconfiguring the runner.
GITEA_TOKEN is present, so the auth half of Question 2 is already answered.
Also 17 TB free, so disk was never a constraint.
What this changes
Adds one step that downloads the static docker CLI and checks it can reach the daemon through that socket. Static binary rather than apt: one download, no package index, no repository to add, and only the client is wanted since the daemon already exists.
The architecture is resolved with uname -m, not hardcoded. Nothing in this repository has ever confirmed what the NAS is. Assuming x86_64 would waste a whole run on the one machine everything else queues behind — the same class of mistake as assuming Portainer's build context contained .git, which is what #235 was.
That step is deliberately notcontinue-on-error, unlike the diagnostics above it. If the client cannot be installed there is nothing left to measure, and one clear failure reads better than three skipped steps.
What we still do not know
Does the registry accept a push?
What does a real image build cost on this runner?
Those are the two numbers that decide whether the whole approach is worth having.
Worth saying before you merge
My recommendation is still that per-merge image builds are the wrong shape here, whatever this run reports. A full CI pass is ~21.5 minutes on one serialised runner, the queue hit six deep yesterday, and the thing being bought is seven characters on an admin screen — while builtAt already answers the question that actually bit you.
If the answers come back positive, the design I would propose is release-triggered builds, not per-merge: build and push when a version is cut, Portainer pulls that tag. Same benefit, none of the queue pressure, and it pairs with the sonar.projectVersion bump that already belongs at release time.
This run is worth doing because it turns that recommendation from a guess into a decision backed by numbers.
Iteration 2 of the throwaway spike. One file, still `workflow_dispatch` only, still deleted when #237 is answered.
## What the first run told us
```
NO BUILDX
--- socket ---
srw-rw---- 1 root root 0 /var/run/docker.sock
GITEA_TOKEN is present
docker: command not found ← exit 127
```
**Question 1 answered: no, the runner cannot build images as configured.** The job container has no `docker` binary, so login failed and the three steps that mattered were skipped — no answer on whether the registry accepts a push, and no build time.
Two things it did settle, both useful:
- **`/var/run/docker.sock` is mounted**, so the daemon is reachable and only the *client* is missing. That is closable from inside a step, without reconfiguring the runner.
- **`GITEA_TOKEN` is present**, so the auth half of Question 2 is already answered.
Also 17 TB free, so disk was never a constraint.
## What this changes
Adds one step that downloads the static `docker` CLI and checks it can reach the daemon through that socket. Static binary rather than `apt`: one download, no package index, no repository to add, and only the client is wanted since the daemon already exists.
**The architecture is resolved with `uname -m`, not hardcoded.** Nothing in this repository has ever confirmed what the NAS is. Assuming `x86_64` would waste a whole run on the one machine everything else queues behind — the same class of mistake as assuming Portainer's build context contained `.git`, which is what #235 was.
That step is deliberately **not** `continue-on-error`, unlike the diagnostics above it. If the client cannot be installed there is nothing left to measure, and one clear failure reads better than three skipped steps.
## What we still do not know
- Does the registry accept a push?
- What does a real image build cost on this runner?
Those are the two numbers that decide whether the whole approach is worth having.
## Worth saying before you merge
My recommendation is still that **per-merge image builds are the wrong shape here**, whatever this run reports. A full CI pass is ~21.5 minutes on one serialised runner, the queue hit six deep yesterday, and the thing being bought is seven characters on an admin screen — while `builtAt` already answers the question that actually bit you.
If the answers come back positive, the design I would propose is **release-triggered** builds, not per-merge: build and push when a version is cut, Portainer pulls that tag. Same benefit, none of the queue pressure, and it pairs with the `sonar.projectVersion` bump that already belongs at release time.
This run is worth doing because it turns that recommendation from a guess into a decision backed by numbers.
Ref #237
The first run answered Question 1 with a flat no. The job container has no docker binary, so login died with exit 127 and the three steps that mattered were skipped — no answer on whether the registry accepts a push, and no build time.
What it did establish is that /var/run/docker.sock is mounted. The daemon is reachable; only the client is missing. That is a gap a step can close without reconfiguring the runner, so it is worth one more iteration before deciding anything.
The static binary rather than apt: one download, no package index to refresh, no repository to add, and only the CLI is wanted since the daemon already exists on the other side of that socket.
The architecture is resolved with uname rather than hardcoded. Nothing in this repository has ever confirmed what the NAS is, and assuming x86_64 would waste a whole run on the one machine everything else queues behind — the same class of mistake as assuming Portainer's build context contained .git in #235.
This step is deliberately not continue-on-error, unlike the diagnostics above it. If the client cannot be installed there is nothing left to measure, and one clear failure reads better than three skipped steps.
Still workflow_dispatch only, still bounded by timeout-minutes, still deleted when #237 is answered.
Ref #237
Iteration 2 got as far as the registry and was refused with a 401, not a certificate error, which established that everything except the credential already worked: the docker CLI installs, the daemon is reachable through the mounted socket, the container registry answers on /v2/, and TLS to the Gitea host is trusted from the runner.
The token Actions injects automatically is scoped for the repository API and is not accepted by the package registry. This switches to REGISTRY_TOKEN, a personal access token carrying write:package, which is the one thing the workflow could not arrange for itself.
Ref #237
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Iteration 2 of the throwaway spike. One file, still
workflow_dispatchonly, still deleted when #237 is answered.What the first run told us
Question 1 answered: no, the runner cannot build images as configured. The job container has no
dockerbinary, so login failed and the three steps that mattered were skipped — no answer on whether the registry accepts a push, and no build time.Two things it did settle, both useful:
/var/run/docker.sockis mounted, so the daemon is reachable and only the client is missing. That is closable from inside a step, without reconfiguring the runner.GITEA_TOKENis present, so the auth half of Question 2 is already answered.Also 17 TB free, so disk was never a constraint.
What this changes
Adds one step that downloads the static
dockerCLI and checks it can reach the daemon through that socket. Static binary rather thanapt: one download, no package index, no repository to add, and only the client is wanted since the daemon already exists.The architecture is resolved with
uname -m, not hardcoded. Nothing in this repository has ever confirmed what the NAS is. Assumingx86_64would waste a whole run on the one machine everything else queues behind — the same class of mistake as assuming Portainer's build context contained.git, which is what #235 was.That step is deliberately not
continue-on-error, unlike the diagnostics above it. If the client cannot be installed there is nothing left to measure, and one clear failure reads better than three skipped steps.What we still do not know
Those are the two numbers that decide whether the whole approach is worth having.
Worth saying before you merge
My recommendation is still that per-merge image builds are the wrong shape here, whatever this run reports. A full CI pass is ~21.5 minutes on one serialised runner, the queue hit six deep yesterday, and the thing being bought is seven characters on an admin screen — while
builtAtalready answers the question that actually bit you.If the answers come back positive, the design I would propose is release-triggered builds, not per-merge: build and push when a version is cut, Portainer pulls that tag. Same benefit, none of the queue pressure, and it pairs with the
sonar.projectVersionbump that already belongs at release time.This run is worth doing because it turns that recommendation from a guess into a decision backed by numbers.
Ref #237
Pull request closed