Compare commits
8
Commits
0c3a55a8cf
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4350f40b5a | ||
|
|
1452c4ac96 | ||
|
|
2e2438672f | ||
|
|
736319458c | ||
|
|
adbc3cd6e7 | ||
|
|
08b0fedf6f | ||
|
|
f0307c6558 | ||
|
|
a6645add28 |
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"mattpocock-skills@claude-plugins-official": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
# Why act_runner's Docker volumes accumulate, and how to clean them up
|
||||
|
||||
> **Scope note:** this is about the **host/runner infrastructure** for
|
||||
> git.arthurerlich.de's Gitea Actions (`act_runner` + the Docker daemon it
|
||||
> drives, in the docker-compose setup for that Gitea instance) — **not**
|
||||
> about this repo's own `Dockerfile`/published image. This repo's
|
||||
> `publish.yml` already runs `docker system prune -af --volumes` before its
|
||||
> build for the same underlying reason described here (unbounded volume/image
|
||||
> growth on the CI host), which is what prompted this research. No access to
|
||||
> the actual Gitea docker-compose file was available or used; every claim
|
||||
> below is grounded in act_runner's/Gitea's/Docker's own docs and source, or
|
||||
> flagged as inferred/secondary where it isn't.
|
||||
|
||||
## Recommendations (ranked)
|
||||
|
||||
1. **Schedule `docker system prune -af --volumes` on the runner host on a
|
||||
recurring basis (cron or a scheduled Gitea Action), the same pattern this
|
||||
repo's own `publish.yml` already uses before its build.** — **Proven
|
||||
mechanism, standard mitigation.** No built-in act_runner/Gitea equivalent
|
||||
exists (see §2); this is the documented, generic Docker-level fix and is
|
||||
what community guidance for this exact runner-volume-leak class of
|
||||
problem converges on. Prefer a filtered form if the host also runs
|
||||
same-day rebuilds you don't want swept up:
|
||||
`docker system prune -af --volumes --filter "until=24h"` (or
|
||||
`docker volume prune --filter "until=24h"` for a volumes-only, less
|
||||
aggressive sweep) — see §2 for the exact filter semantics.
|
||||
2. **Confirm/enable act_runner's `container.valid_volumes` allowlist and
|
||||
review `container.options`/`container.network`, but do not expect them to
|
||||
fix the leak** — **Proven from act_runner's own config reference (§1.2):
|
||||
these control what containers are *allowed* to mount, not automatic
|
||||
cleanup after a job ends.** Leaving `valid_volumes` wide open
|
||||
(`- '**'`) doesn't itself cause more leakage than default, but tightening
|
||||
it is good hygiene independent of the disk problem.
|
||||
3. **If DinD (Docker-in-Docker) mode is in use, that is very likely where
|
||||
the bulk of the leaked volumes come from** — **inferred, but from a
|
||||
documented, closely-analogous mechanism (§1), not confirmed against this
|
||||
host's actual config** since no docker-compose file was available. DinD
|
||||
needs its own persistent Docker data directory (commonly volume-backed),
|
||||
and any job container that declares a `VOLUME` in its Dockerfile gets an
|
||||
anonymous volume that Docker does not remove on `docker rm` unless the
|
||||
container was started with `--rm` — a mechanism GitHub's own
|
||||
`actions/runner` maintainers confirmed by design for the conceptually
|
||||
identical GitHub Actions Docker executor (§1.3). act_runner's job/step
|
||||
containers are created, started, stopped, and removed as separate Docker
|
||||
API calls (not a single `docker run --rm`), which is the same shape of
|
||||
gap.
|
||||
4. **A dedicated Linux user for act_runner (and rootless Docker specifically)
|
||||
is a security/isolation improvement, not a fix for volume accumulation.**
|
||||
— **Proven boundary, directly stated in act_runner's own docs (§3).**
|
||||
Do it for blast-radius/`docker.sock`-exposure reasons; don't expect it to
|
||||
change disk usage. Running the Docker daemon itself in **rootless mode**
|
||||
changes *where* volumes live (per-user data root under
|
||||
`$XDG_RUNTIME_DIR`/`~/.local/share/docker` instead of `/var/lib/docker`)
|
||||
but Docker's own rootless docs describe no change to prune/cleanup
|
||||
semantics — unused volumes still only go away via an explicit prune,
|
||||
run as that user against that daemon.
|
||||
|
||||
**Net: there is no act_runner/Gitea-side switch that stops the leak.** The
|
||||
fix is operational — a scheduled prune — same shape as the one already
|
||||
present in this repo's own `publish.yml`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why this happens
|
||||
|
||||
### 1.1 act_runner's job execution model
|
||||
|
||||
Per Gitea's own Act Runner documentation, act_runner supports two execution
|
||||
modes: **Docker container mode** ("it is recommended to run jobs in a
|
||||
docker container", requiring a running Docker daemon) and **host mode**
|
||||
(jobs run directly on the machine act_runner is on, no Docker involved).
|
||||
Docker mode is the default/recommended mode and is what a normal
|
||||
docker-compose Gitea+act_runner setup uses.
|
||||
([docs.gitea.com/1.23/usage/actions/act-runner](https://docs.gitea.com/1.23/usage/actions/act-runner/))
|
||||
|
||||
In Docker mode, act_runner creates a container per job (and per
|
||||
service-container / step container as declared in the workflow), runs it,
|
||||
and removes it — as separate Docker API calls, not a single atomic
|
||||
`docker run --rm ...`. This matters because Docker's own volume-removal
|
||||
rule is tied to *how* a container is removed, not just that it's gone (see
|
||||
§1.3).
|
||||
|
||||
### 1.2 act_runner's `container.*` config settings
|
||||
|
||||
From act_runner's own `config.yaml` reference (fetched from
|
||||
`gitea.com/gitea/act_runner`'s example config,
|
||||
[gitea.com/gitea/act_runner — config.example.yaml](https://gitea.com/gitea/act_runner/raw/branch/main/internal/pkg/config/config.example.yaml)):
|
||||
|
||||
- **`container.docker_host`** — overrides the Docker daemon act_runner talks
|
||||
to. Empty (default) auto-detects; `-` auto-detects without mounting the
|
||||
socket into job containers. Controls *which* daemon runs jobs, not
|
||||
cleanup.
|
||||
- **`container.network`** — `host`, `bridge`, or a custom network name;
|
||||
empty auto-creates a per-job network. Unrelated to volumes.
|
||||
- **`container.privileged`** — "Privileged mode is required for
|
||||
Docker-in-Docker." This is the flag that turns on DinD-style job
|
||||
execution.
|
||||
- **`container.options`** — free-form extra flags passed to container
|
||||
creation (e.g. `--add-host=...`, or extra `--volume` mounts). Whatever a
|
||||
workflow or admin puts here can itself add more bind mounts/volumes per
|
||||
job, but is opt-in, not a default source of leakage.
|
||||
- **`container.valid_volumes`** — an **allowlist** (glob syntax) of
|
||||
volumes/bind mounts job containers are permitted to mount; empty means
|
||||
none permitted, `'**'` means any. This is an authorization control, not a
|
||||
lifecycle/cleanup control — it limits what *can* be mounted, and doesn't
|
||||
cause or prevent leftover anonymous volumes from images that declare
|
||||
`VOLUME` internally.
|
||||
- **`container.bind_workdir`** — relevant specifically to DinD: "required
|
||||
for Docker-in-Docker (DinD) setups when jobs use docker compose with bind
|
||||
mounts (e.g. `.:/app`), as volume-based workspaces are not accessible
|
||||
from the DinD daemon's filesystem." Confirms DinD's workspace handling is
|
||||
a distinct code path from plain Docker mode.
|
||||
- **`workdir_cleanup_age` / `idle_cleanup_interval`** — these exist, but per
|
||||
the config reference they clean up **act_runner's own workspace
|
||||
directories** (stale bind-mounted task-workdir folders on the host
|
||||
filesystem, and orphaned host-mode scratch dirs) — **not Docker volumes**.
|
||||
There is no equivalent `container`-level setting for pruning Docker
|
||||
volumes.
|
||||
|
||||
None of act_runner's documented settings amount to "clean up Docker volumes
|
||||
after a job." The closest things (`workdir_cleanup_age`,
|
||||
`idle_cleanup_interval`) are scoped to filesystem workdirs, a different
|
||||
mechanism from Docker volume objects.
|
||||
|
||||
### 1.3 Is DinD specifically the source of the leak, vs plain Docker mode?
|
||||
|
||||
Two independent, additive causes are documented:
|
||||
|
||||
1. **Any Docker execution mode (plain or DinD): anonymous volumes from
|
||||
job/service containers leak by Docker's own removal semantics.** This is
|
||||
not act_runner-specific — it's how Docker containers work. GitHub's own
|
||||
`actions/runner` maintainers documented the identical failure mode for
|
||||
GitHub Actions' (conceptually equivalent) Docker executor: *"When using
|
||||
service containers with a `VOLUME` declaration in their Dockerfile, an
|
||||
anonymous volume is automatically created with the container. At the end
|
||||
of the workflow, the container is stopped and removed, but the anonymous
|
||||
volumes it created stay around... The runner creates, starts, stops and
|
||||
removes containers with separate commands, so passing `--rm` to `docker
|
||||
create` has no effect on `docker remove`."*
|
||||
([actions/runner#1885](https://github.com/actions/runner/issues/1885) —
|
||||
maintainer-confirmed root cause, not community speculation). act_runner's
|
||||
job execution is architecturally the same shape (separate create/start/
|
||||
stop/remove calls against the Docker API), so the same leak mechanism
|
||||
applies to it by design, absent extra config — this is the strongest
|
||||
available evidence, though it is evidence from GitHub's runner, applied
|
||||
by analogy to act_runner rather than a direct act_runner maintainer
|
||||
statement (no act_runner-specific issue making this exact claim was
|
||||
found in this pass).
|
||||
2. **DinD adds its own persistent data directory on top.** A Docker-in-Docker
|
||||
daemon needs its own `/var/lib/docker`-equivalent to store the images/
|
||||
layers/volumes *of the jobs it runs*, and community DinD setups
|
||||
routinely back that directory with a Docker volume so it survives
|
||||
restarts of the DinD container itself (e.g. "`/var/lib/docker` cannot be
|
||||
on AUFS, so it needs to be made a volume" — a long-standing operational
|
||||
note about DinD from general Docker-in-Docker tooling, not Gitea-specific,
|
||||
flagged here as secondary/community evidence). This is additive to (1):
|
||||
DinD setups accumulate both the generic per-job anonymous-volume leak
|
||||
*and* whatever grows inside DinD's own backing store (images, build
|
||||
cache, and volumes belonging to the containers it spawns), which is a
|
||||
second, larger accumulation surface than plain Docker mode.
|
||||
|
||||
**Conclusion:** the generic anonymous-volume leak happens in plain Docker
|
||||
mode too (it's a Docker container-removal semantics issue, not a DinD-only
|
||||
bug), but DinD mode is very likely the bigger contributor on a real host,
|
||||
because it adds a whole second Docker data root that grows independently.
|
||||
Without the actual docker-compose file, it isn't possible to confirm from
|
||||
this pass alone whether git.arthurerlich.de's act_runner is configured for
|
||||
DinD (`container.privileged: true` set) or plain Docker mode.
|
||||
|
||||
---
|
||||
|
||||
## 2. How to clean up
|
||||
|
||||
### 2.1 `docker volume prune` / `docker system prune` semantics (Docker's own docs)
|
||||
|
||||
Per Docker's official pruning reference
|
||||
([docs.docker.com/engine/manage-resources/pruning](https://docs.docker.com/engine/manage-resources/pruning/),
|
||||
[docs.docker.com/reference/cli/docker/volume/prune](https://docs.docker.com/reference/cli/docker/volume/prune/),
|
||||
[docs.docker.com/reference/cli/docker/system/prune](https://docs.docker.com/reference/cli/docker/system/prune/)):
|
||||
|
||||
- **`docker system prune`** (no flags) removes stopped containers, networks
|
||||
not used by at least one container, dangling images, and unused build
|
||||
cache. **Volumes are excluded by default.**
|
||||
- **`docker system prune --volumes`** (or `-a --volumes` to also sweep
|
||||
non-dangling unused images) additionally removes "all volumes not used by
|
||||
at least one container."
|
||||
- **`docker volume prune`** removes unused local volumes; by default this
|
||||
means **anonymous** volumes not referenced by any container. `--all`
|
||||
extends this to unused **named** volumes too.
|
||||
- **`docker volume prune --filter "until=<duration>"`** narrows the sweep to
|
||||
volumes older than the given age (still only unused ones) — a way to keep
|
||||
volumes from a job that finished minutes ago while still cleaning up
|
||||
older leftovers.
|
||||
|
||||
**Safety on a host mid-job:** all of the above operate only on resources
|
||||
**not referenced by any container, running or stopped**. A volume mounted
|
||||
into a currently-running job container is never touched. This makes it safe
|
||||
to run on a schedule against a live runner host without coordinating with
|
||||
in-flight jobs — the documented behavior is conservative by construction,
|
||||
not merely "usually fine in practice."
|
||||
|
||||
### 2.2 Does act_runner or Gitea ship any built-in cleanup for this?
|
||||
|
||||
No. Searched act_runner's config reference, Gitea's Actions/runner
|
||||
documentation, and the two most relevant issue threads found
|
||||
([go-gitea/gitea#31457](https://github.com/go-gitea/gitea/issues/31457),
|
||||
about `act_runner` manageability generally, and
|
||||
[gitea.com/gitea/runner#167](https://gitea.com/gitea/runner/issues/167) /
|
||||
[go-gitea/gitea#24438](https://github.com/go-gitea/gitea/issues/24438),
|
||||
about `docker.sock` exposure) — none describe a built-in Docker volume
|
||||
cleanup mechanism, cron-style prune config, or maintainer statement
|
||||
addressing this exact problem. The only cleanup knobs act_runner documents
|
||||
(`workdir_cleanup_age`, `idle_cleanup_interval`) are scoped to its own
|
||||
host-filesystem workdirs, not Docker volume objects (§1.2). This absence is
|
||||
itself informative: there is nothing to "turn on" here — a scheduled
|
||||
external prune is the only available lever.
|
||||
|
||||
### 2.3 Is a scheduled `docker system prune -af --volumes` the standard approach?
|
||||
|
||||
Yes, and it is corroborated by both this repo's own precedent and general
|
||||
community guidance:
|
||||
|
||||
- This repo's `publish.yml` already runs `docker system prune -af --volumes`
|
||||
before its build, for the same underlying class of problem (disk
|
||||
exhaustion from accumulated Docker state on a CI runner) — direct
|
||||
precedent already in this codebase.
|
||||
- General community guidance for self-hosted CI runners converges on the
|
||||
same shape of fix: a scheduled/cron `docker system prune` (optionally
|
||||
filtered by age) run on the runner host. This is consistent, secondary
|
||||
evidence (blog posts / community write-ups, not a Docker or Gitea
|
||||
standards document) but aligns with what Docker's own docs make safe
|
||||
(§2.1).
|
||||
|
||||
**A more targeted alternative** — `docker volume prune --filter "until=24h"`
|
||||
run on a schedule (e.g. nightly cron) — trades some aggressiveness for
|
||||
narrower scope: it only ever removes volumes, leaves stopped
|
||||
containers/networks/images alone, and skips anything from the last 24h in
|
||||
case a long-running or recently-finished job's volume is still wanted for
|
||||
debugging. This is a reasonable middle ground if a full `system prune -af`
|
||||
feels too broad for the runner host (e.g. if other non-CI Docker workloads
|
||||
share the machine); `docker system prune -af --volumes` is simpler and is
|
||||
what this repo already does elsewhere.
|
||||
|
||||
---
|
||||
|
||||
## 3. Dedicated user for the runner: security concern, not a cleanup mechanism
|
||||
|
||||
### 3.1 act_runner's own guidance
|
||||
|
||||
Gitea's Act Runner docs (systemd install instructions) recommend creating a
|
||||
dedicated unprivileged `act_runner` user, and note that adding it to the
|
||||
`docker` group (needed for Docker mode) **"effectively gives act_runner
|
||||
root access to the system"** — framed entirely as a privilege/blast-radius
|
||||
concern.
|
||||
([docs.gitea.com/1.23/usage/actions/act-runner](https://docs.gitea.com/1.23/usage/actions/act-runner/))
|
||||
|
||||
This is echoed more sharply in Gitea's own tracker:
|
||||
[gitea.com/gitea/runner#167](https://gitea.com/gitea/runner/issues/167) /
|
||||
mirrored as [go-gitea/gitea#24438](https://github.com/go-gitea/gitea/issues/24438),
|
||||
titled *"Gitea Actions is HIGHLY insecure due to binding of docker.sock into
|
||||
all containers (= root on host)"* — the concern raised (and act_runner's own
|
||||
`examples/vm/rootless-docker.md` walkthrough, built specifically in
|
||||
response to this class of concern) is **container escape / arbitrary root
|
||||
on the host via `docker.sock` exposure to untrusted job code**, not disk
|
||||
usage.
|
||||
|
||||
### 3.2 Rootless Docker / user-namespace remapping (Docker's own docs)
|
||||
|
||||
Per Docker's official rootless-mode docs
|
||||
([docs.docker.com/engine/security/rootless](https://docs.docker.com/engine/security/rootless/)):
|
||||
rootless mode "lets you run the Docker daemon and containers as a non-root
|
||||
user to mitigate potential vulnerabilities in the daemon and the container
|
||||
runtime" — again, a security-isolation feature. The docs describe no
|
||||
special relationship to volume/disk accumulation or cleanup behavior.
|
||||
act_runner's own `rootless-docker.md` walkthrough
|
||||
([gitea.com/gitea/act_runner — examples/vm/rootless-docker.md](https://gitea.com/gitea/act_runner/raw/branch/main/examples/vm/rootless-docker.md))
|
||||
is, in its own words, exclusively about security/isolation setup (dedicated
|
||||
user, rootless daemon, systemd units) and makes no mention of volume
|
||||
cleanup or storage management at any point.
|
||||
|
||||
Practically, rootless mode does shift *where* Docker's data root (and
|
||||
therefore its volumes) lives — per-user, typically under
|
||||
`$XDG_RUNTIME_DIR` / `~/.local/share/docker` rather than the system-wide
|
||||
`/var/lib/docker` — but this only changes the location being filled up, not
|
||||
whether it fills up. The same `docker volume prune` semantics (§2.1) still
|
||||
apply, run as that user against that user's daemon.
|
||||
|
||||
### 3.3 Conclusion
|
||||
|
||||
- **(a) Dedicated Linux user for the act_runner service itself:** purely a
|
||||
blast-radius/permissions control (who can touch what if the runner or a
|
||||
job container is compromised). No documented bearing on volume
|
||||
accumulation.
|
||||
- **(b) Rootless Docker / userns-remap:** also purely a security-isolation
|
||||
mechanism per Docker's own docs. It relocates where volumes are stored
|
||||
(per-user vs system-wide) but does not change prune semantics or provide
|
||||
any automatic cleanup — the volume-growth problem is orthogonal to this
|
||||
and is not solved by adopting either (a) or (b).
|
||||
|
||||
Both are worth doing for `docker.sock`-exposure/security reasons
|
||||
independent of this investigation, but neither should be expected to move
|
||||
the disk-usage needle — only a scheduled prune (§1 recommendation #1, §2.3)
|
||||
addresses that.
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
- act_runner config reference — [gitea.com/gitea/act_runner: config.example.yaml](https://gitea.com/gitea/act_runner/raw/branch/main/internal/pkg/config/config.example.yaml)
|
||||
- Gitea Act Runner docs — [docs.gitea.com/1.23/usage/actions/act-runner](https://docs.gitea.com/1.23/usage/actions/act-runner/)
|
||||
- act_runner rootless Docker walkthrough — [gitea.com/gitea/act_runner: examples/vm/rootless-docker.md](https://gitea.com/gitea/act_runner/raw/branch/main/examples/vm/rootless-docker.md)
|
||||
- Gitea Actions `docker.sock` security issue — [gitea.com/gitea/runner#167](https://gitea.com/gitea/runner/issues/167) / mirrored [go-gitea/gitea#24438](https://github.com/go-gitea/gitea/issues/24438)
|
||||
- act_runner manageability issue — [go-gitea/gitea#31457](https://github.com/go-gitea/gitea/issues/31457) (no maintainer disk/volume-cleanup discussion found in this thread)
|
||||
- Anonymous volume leak, maintainer-confirmed root cause (GitHub's `actions/runner`, conceptually equivalent Docker executor) — [actions/runner#1885](https://github.com/actions/runner/issues/1885)
|
||||
- Docker pruning reference — [docs.docker.com/engine/manage-resources/pruning](https://docs.docker.com/engine/manage-resources/pruning/)
|
||||
- `docker volume prune` reference — [docs.docker.com/reference/cli/docker/volume/prune](https://docs.docker.com/reference/cli/docker/volume/prune/)
|
||||
- `docker system prune` reference — [docs.docker.com/reference/cli/docker/system/prune](https://docs.docker.com/reference/cli/docker/system/prune/)
|
||||
- Docker rootless mode — [docs.docker.com/engine/security/rootless](https://docs.docker.com/engine/security/rootless/)
|
||||
@@ -0,0 +1,182 @@
|
||||
# Does official Blender run under Alpine/musl?
|
||||
|
||||
Research for [wayfinder ticket #5](https://git.arthurerlich.de/haylan/godot-ci/issues/5).
|
||||
Scope: this repo currently downloads the **official glibc Linux tarball** from
|
||||
`download.blender.org/release/` (see `Dockerfile`, `scripts/resolve-blender-url.sh`)
|
||||
onto a `barichello/godot-ci` (Ubuntu/glibc) base, and uses it for headless
|
||||
`.blend` → asset import in the Godot CI pipeline. This doc asks whether that
|
||||
same tarball, or any Blender build, works on an Alpine/musl base.
|
||||
|
||||
## Verdict
|
||||
|
||||
- **Official tarball on Alpine as-is: does not work.** It is a glibc build
|
||||
(Blender requires glibc ≥ 2.28 per Blender's own requirements page) and
|
||||
Alpine ships musl instead of glibc, with no glibc present at all.
|
||||
- **Official tarball via `gcompat`: unproven / likely does not work reliably.**
|
||||
`gcompat` is explicitly a shim for *pre-built* glibc binaries and is
|
||||
documented by Alpine as not fully compatible — "some [binaries] will still
|
||||
not work with this patch." The one concrete community attempt found (a PR
|
||||
against `ikester/blender-docker`) tried exactly this — official binary +
|
||||
Alpine + presumably gcompat/manual lib shims — and failed with missing
|
||||
libraries (`libGLU.so.1`, `libXrender.so.1`) and unresolved glibc-only
|
||||
symbols (`__sched_cpualloc`, `qsort_r`, `backtrace`). The image never ran.
|
||||
This is a single secondary data point, not exhaustive, but it's the only
|
||||
real-world attempt this research turned up and it failed outright.
|
||||
- **musl-native alternative exists: yes.** Alpine's `community` repository
|
||||
packages Blender from source against musl — `blender`, `blender-headless`,
|
||||
`blender-shared`, `blender-doc`, `py3-blender` — currently at **5.2.0-r0**
|
||||
for `x86_64`. `blender-headless` is specifically the no-GUI server/CLI
|
||||
build, i.e. exactly the shape needed for a Godot import pipeline
|
||||
(`blender --background --python ...`).
|
||||
- **Caveat on the alternative:** as of this research, the Alpine package
|
||||
index shows these packages **only in the `edge` branch**, not in a
|
||||
released stable branch (e.g. `v3.20`/`v3.21`). Edge is Alpine's rolling/
|
||||
unstable branch — usable, but it means pinning to a moving target rather
|
||||
than a versioned stable release, which cuts against the reproducibility
|
||||
this pipeline wants from a pinned Blender version.
|
||||
- **Headless `.blend` import specifically:** not separately verified beyond
|
||||
the existence of `blender-headless` and its description ("headless build"
|
||||
for server/automation use). No primary Blender-side documentation or
|
||||
Alpine changelog was found confirming Python-scripted `--background`
|
||||
import/export works flawlessly on the musl build; this is inferred from
|
||||
the package's stated purpose, not confirmed by a run.
|
||||
|
||||
**Net:** there is a plausible musl-native path (Alpine `community/edge`
|
||||
`blender-headless`), but it is edge-only today, and the "just run the
|
||||
official tarball via gcompat" path has a real failure report against it.
|
||||
Either the pipeline pins to Alpine `edge` (accepting rolling-release churn)
|
||||
or Alpine is not a good fit for this image's need for a stable, pinned
|
||||
Blender version.
|
||||
|
||||
## Details and sources
|
||||
|
||||
### Official Blender's own requirement (primary)
|
||||
|
||||
Blender's official download requirements page (blender.org) states Linux
|
||||
support requires **glibc 2.28 or newer**; for older glibc (e.g. glibc 2.17 /
|
||||
RHEL 7) Blender recommends staying on the old Blender 3.4 release instead.
|
||||
This is a hard glibc dependency stated by Blender itself — nothing on that
|
||||
page mentions musl or Alpine as a supported target.
|
||||
(https://www.blender.org/download/requirements/, https://www.blender.org/download/)
|
||||
|
||||
Blender's Linux build documentation
|
||||
(https://developer.blender.org/docs/handbook/building_blender/linux/)
|
||||
returned HTTP 403 to automated fetch during this research and could not be
|
||||
quoted directly; it is generally understood (and consistent with the glibc
|
||||
requirement above) to target mainstream glibc distros for both building and
|
||||
running Blender, with no documented musl/Alpine build target.
|
||||
|
||||
### Alpine's gcompat (primary — Alpine wiki/GitHub)
|
||||
|
||||
- Alpine's own wiki page on running glibc programs
|
||||
(`https://wiki.alpinelinux.org/wiki/Running_glibc_programs`) describes
|
||||
`gcompat` as the mechanism for running *already-compiled* glibc binaries
|
||||
on musl by intercepting the glibc dynamic linker path and redirecting to
|
||||
musl plus a set of compatibility shims. (Fetched via search-engine summary
|
||||
— the wiki itself returned HTTP 403 to direct fetch during this research.)
|
||||
- The `gcompat` project itself (https://github.com/rs--/gcompat, mirrored at
|
||||
https://github.com/Stantheman/gcompat) states plainly: it works for
|
||||
binaries **already compiled against glibc**, has **no headers**, and
|
||||
**cannot be used to build software that requires glibc**. It explicitly
|
||||
warns that "some [glibc binaries] will still not work with this patch,"
|
||||
and recommends fixing/porting software to be libc-portable instead of
|
||||
relying on gcompat for anything beyond simple/small binaries.
|
||||
- Alpine's `gcompat` package on `pkgs.alpinelinux.org` is present in the
|
||||
`main` repo, version `1.1.0-r4` (checked build date 2023-07-31) — small
|
||||
and simple, consistent with its stated scope (a shim, not a glibc
|
||||
reimplementation). (https://pkgs.alpinelinux.org/packages?name=gcompat)
|
||||
|
||||
Blender is a large, complex application (OpenGL/Vulkan, audio via
|
||||
PulseAudio/PipeWire, X11, dozens of bundled C/C++ libraries, a bundled
|
||||
Python). This is exactly the profile gcompat's own documentation flags as
|
||||
likely to fail — and the one concrete attempt found (below) did fail.
|
||||
|
||||
### Alpine's musl-native Blender package (primary — pkgs.alpinelinux.org)
|
||||
|
||||
`pkgs.alpinelinux.org` package index, checked directly:
|
||||
|
||||
| package | branch | repo | arch | version | purpose |
|
||||
|---|---|---|---|---|---|
|
||||
| `blender` | edge | community | x86_64 | 5.2.0-r0 | full GUI build |
|
||||
| `blender-headless` | edge | community | x86_64 | 5.2.0-r0 | **no-GUI build for server/automation** |
|
||||
| `blender-shared` | edge | community | x86_64 | 5.2.0-r0 | shared libs subpackage |
|
||||
| `blender-doc` | edge | community | x86_64 | 5.2.0-r0 | docs |
|
||||
| `py3-blender` | edge | community | x86_64 | 5.2.0-r0 | Python bindings subpackage |
|
||||
|
||||
(https://pkgs.alpinelinux.org/package/edge/community/x86_64/blender,
|
||||
https://pkgs.alpinelinux.org/package/edge/community/x86_64/blender-headless)
|
||||
|
||||
Since Alpine's `community`/`main` repos are built from source
|
||||
(`aports`) against musl, these are **native musl builds**, not the glibc
|
||||
tarball run through a shim — this is the actual "musl-friendly alternative"
|
||||
the ticket asks about. `blender-headless`'s description ("headless build",
|
||||
no GUI deps) matches the "background import for a CI asset pipeline" use
|
||||
case directly.
|
||||
|
||||
The important caveat, also confirmed directly against the package index:
|
||||
these packages appear to exist **only under the `edge` branch** at the time
|
||||
of this research (no stable-branch listing, e.g. `v3.20`/`v3.21`, was found
|
||||
for `blender`/`blender-headless`). Alpine's `edge` is the rolling
|
||||
development branch — using it means tracking a moving target rather than a
|
||||
pinned stable Alpine release, which works against the kind of reproducible,
|
||||
version-pinned build this repo's `resolve-blender-url.sh` /
|
||||
`resolve-godot-version.sh` scripts are designed around.
|
||||
|
||||
### Community corroboration (secondary)
|
||||
|
||||
- `ikester/blender-docker` PR #1 ("Change to use alpine linux, much smaller
|
||||
base image size", https://github.com/ikester/blender-docker/pull/1):
|
||||
contributor tried moving that Dockerfile to Alpine using the official
|
||||
Blender binary. Result reported by the repo owner: **it did not run** —
|
||||
missing `libGLU.so.1`, `libXrender.so.1`, and unresolved glibc-only
|
||||
symbols (`__sched_cpualloc`, `qsort_r`, `backtrace`). The owner asked the
|
||||
contributor to "sort out dependencies"; the PR was never updated further
|
||||
and was not merged. This is a single, non-exhaustive report (no gcompat
|
||||
usage confirmed explicitly), but it's concrete evidence that the naive
|
||||
"official tarball + Alpine" path fails in practice, consistent with
|
||||
gcompat's own documented limitations.
|
||||
- Other headless-Blender Docker projects found in this search
|
||||
(`BlenderKit/headless-blender-container`, `HaiyiMei/blender-docker-headless`,
|
||||
LinuxServer.io's `docker-blender`) all use glibc-based bases (Ubuntu/Debian),
|
||||
not Alpine — i.e. nobody surveyed here ships a working Alpine+official-tarball
|
||||
image. This absence is itself weak corroborating evidence that the
|
||||
glibc-tarball-on-Alpine path isn't a well-trodden, working setup.
|
||||
|
||||
## Implication for this repo
|
||||
|
||||
If Alpine is being considered as a smaller/leaner base for the CI image:
|
||||
|
||||
1. The current approach (download official glibc tarball from
|
||||
`download.blender.org`, per `Dockerfile` / `scripts/resolve-blender-url.sh`)
|
||||
**cannot be ported to Alpine as-is** — no glibc, and gcompat is not a
|
||||
credible fix for something as large/complex as Blender (per gcompat's own
|
||||
docs, and per the one real attempt found, which failed).
|
||||
2. The only musl-native path found, Alpine's `blender-headless` package,
|
||||
exists **only on `edge`** — adopting Alpine would mean giving up the
|
||||
pinned/reproducible-version approach this repo currently uses for both
|
||||
Godot and Blender, in favor of tracking Alpine's rolling branch, or
|
||||
manually backporting/building the aport for a stable branch (extra
|
||||
maintenance burden not costed here).
|
||||
3. No evidence was found either way (positive or negative) on whether the
|
||||
Alpine `blender-headless` package's Python/`--background` scripting
|
||||
behaves identically to the official build for `.blend` import — only
|
||||
that the package is described as built for that use case.
|
||||
|
||||
**This is a real argument against Alpine** for this image unless the team
|
||||
is willing to (a) pin to Alpine `edge` and accept its churn, or (b) take on
|
||||
maintaining a backport of the `blender`/`blender-headless` aport for a
|
||||
stable Alpine branch themselves.
|
||||
|
||||
## Sources
|
||||
|
||||
- https://www.blender.org/download/requirements/
|
||||
- https://www.blender.org/download/
|
||||
- https://developer.blender.org/docs/handbook/building_blender/linux/ (403 on direct fetch; referenced via search summary)
|
||||
- https://wiki.alpinelinux.org/wiki/Running_glibc_programs (403 on direct fetch; referenced via search summary)
|
||||
- https://github.com/rs--/gcompat
|
||||
- https://github.com/Stantheman/gcompat
|
||||
- https://pkgs.alpinelinux.org/packages?name=gcompat
|
||||
- https://pkgs.alpinelinux.org/package/edge/community/x86_64/blender
|
||||
- https://pkgs.alpinelinux.org/package/edge/community/x86_64/blender-headless
|
||||
- https://github.com/ikester/blender-docker/pull/1 (secondary/community)
|
||||
- https://github.com/BlenderKit/headless-blender-container (secondary, uses Ubuntu — corroborates absence of a working Alpine setup)
|
||||
@@ -0,0 +1,282 @@
|
||||
# Can this repo's Docker build be sped up, and does caching exist for it?
|
||||
|
||||
Research triggered by the real-run timing gap: ~5 min locally vs **~16 min** on
|
||||
the actual runner
|
||||
(run [1493](https://git.arthurerlich.de/haylan/godot-ci/actions/runs/1493)),
|
||||
dominated by the single `apt-get install` layer (~470s, `build-essential`,
|
||||
`mingw-w64`, X11/audio/udev dev headers) and the Godot export
|
||||
templates + Blender downloads.
|
||||
|
||||
## Verdict: **yes — but the fix is "stop deleting the cache", not "add a cache"**
|
||||
|
||||
The runner (`runs-on: ubuntu-latest` in
|
||||
[`.gitea/workflows/publish.yml`](../../.gitea/workflows/publish.yml)) is a
|
||||
**self-hosted, persistent** [act_runner](https://gitea.com/gitea/act_runner)
|
||||
host, not an ephemeral GitHub-style VM — the workflow's own "Free up runner
|
||||
disk" step and its comment only make sense on a persistent box ("*the
|
||||
runner's disk fills up over successive runs until a push … is what finally
|
||||
fails*"; an ephemeral runner starts clean every time and would never
|
||||
accumulate anything to prune). That persistence is exactly what makes plain
|
||||
Docker build-cache (no registry, no extra tooling) viable here — and it's
|
||||
also exactly what today's `docker system prune -af --volumes` step throws
|
||||
away before every single build, per Docker's own docs: `system prune`'s
|
||||
default removal set explicitly includes *"Unused build cache"*, and `-a`
|
||||
widens that to "all unused build cache," not just dangling entries
|
||||
([docs.docker.com/reference/cli/docker/system/prune](https://docs.docker.com/reference/cli/docker/system/prune/)).
|
||||
So right now, **local BuildKit cache cannot survive from one nightly run to
|
||||
the next no matter how the Dockerfile is written** — the prune step deletes
|
||||
it every time, before the build that could have reused it even starts.
|
||||
|
||||
Ranked recommendations:
|
||||
|
||||
1. **Stop nuking the build cache in the prune step.** Replace
|
||||
`docker system prune -af --volumes` with targeted pruning that skips
|
||||
builder cache: `docker container prune -f && docker image prune -af &&
|
||||
docker volume prune -f`, plus a separate, bounded
|
||||
`docker builder prune -f --filter until=24h` (or `--keep-storage=<N>GB`)
|
||||
to cap build-cache growth without wiping cache that's still same-day
|
||||
reusable. This alone is what unlocks everything below — no Dockerfile
|
||||
change helps while the current step runs first. Near-zero risk: it still
|
||||
frees the disk that motivated the step, just not by deleting reusable
|
||||
build cache along with it.
|
||||
2. **Split the `apt-get install` layer from the Godot/Blender download
|
||||
layer in the Dockerfile**, apt first. Right now both live in one giant
|
||||
`RUN` chained with `&&` (`Dockerfile` lines 19–62), so a single-byte
|
||||
change anywhere in that chain — including the Godot/Blender URLs that
|
||||
*do* change nightly — invalidates the apt install too. BuildKit's cache
|
||||
is a linear, per-instruction, content-addressed chain: it reuses a layer
|
||||
only if every instruction up to and including it is byte-identical to a
|
||||
cached run (Docker's build-cache docs: cache is invalidated "from that
|
||||
point" the instant one instruction's content changes,
|
||||
[docs.docker.com/build/cache](https://docs.docker.com/build/cache/)).
|
||||
The apt package list changes essentially never; `GODOT_VERSION`/
|
||||
`BLENDER_URL` change most nights. Splitting them means the ~470s apt
|
||||
layer becomes a cache hit on every night that doesn't touch the
|
||||
Dockerfile itself, while the Godot/Blender layer keeps rebuilding (it has
|
||||
to — new content every time a version bumps). Combined with #1, this is
|
||||
the actual fix for the dominant cost in the real run.
|
||||
3. **Add `RUN --mount=type=cache` for apt's own directories**
|
||||
(`/var/cache/apt`, `/var/lib/apt`, `sharing=locked`) as a smaller
|
||||
supplement to #2 — it persists apt's downloaded `.deb` cache independent
|
||||
of the Dockerfile-instruction cache chain, so even a Dockerfile edit that
|
||||
invalidates the layer doesn't force re-downloading every `.deb` from
|
||||
Debian's mirrors, only re-running `dpkg` against the still-local cache
|
||||
([docs.docker.com/reference/dockerfile/#run---mounttypecache](https://docs.docker.com/reference/dockerfile/#run---mounttypecache)).
|
||||
Worth doing alongside #2 since it's a few lines, but it's a hedge against
|
||||
Dockerfile churn, not the main win — #2 already gets full cache hits on
|
||||
the common case (no Dockerfile change, only build-args change).
|
||||
4. **Skip registry-based cache (`--cache-to/--cache-from type=registry`).**
|
||||
Not worth it here. Registry cache exists to share a build cache *across
|
||||
machines that don't share disk* — ephemeral GitHub-hosted runners, fleets
|
||||
of self-hosted runners, etc.
|
||||
([docs.docker.com/build/cache/backends/registry](https://docs.docker.com/build/cache/backends/registry/)).
|
||||
This repo has exactly one persistent runner with its own disk, so #1–#3
|
||||
already give it that same cache locally, for free, with no push/pull
|
||||
round-trip. Pushing a *separate* cache image on top would add push/pull
|
||||
time on every run for a benefit local cache already provides, and it's a
|
||||
bad fit for this specific image's shape: the Godot/Blender layers are
|
||||
multi-GB and, by design, different nearly every night (new versions),
|
||||
so `mode=max` would repeatedly re-push multi-GB "cache" blobs that are
|
||||
very unlikely to ever be pulled back down. There's also an open question
|
||||
of whether it would even work: Gitea's container registry only gained
|
||||
general OCI-artifact support in 1.24
|
||||
([go-gitea/gitea#25846](https://github.com/go-gitea/gitea/issues/25846),
|
||||
closed via PR #34666) — before that it rejected non-image manifests with
|
||||
`"unsupported: Schema version is not supported"`. This repo's Gitea
|
||||
instance version wasn't confirmed as part of this research, so pushing a
|
||||
buildx cache manifest (which uses custom OCI media types, distinct from
|
||||
a normal image manifest) is not guaranteed to work without checking that
|
||||
first.
|
||||
5. **Gitea Actions' `actions/cache` action is the wrong tool for this,
|
||||
noted for completeness.** Gitea Actions does support a GitHub-compatible
|
||||
`actions/cache@v3`/`v4` via a cache server that ships enabled by default
|
||||
in `act_runner`
|
||||
([about.gitea.com tutorial](https://about.gitea.com/resources/tutorials/enable-gitea-actions-cache-to-accelerate-cicd/);
|
||||
[docs.gitea.com/usage/actions/overview](https://docs.gitea.com/usage/actions/overview/)),
|
||||
plus a separate `RUNNER_TOOL_CACHE` mechanism. Both are key/value or
|
||||
directory caches meant for things like `node_modules`/pip wheels between
|
||||
workflow steps — not a Docker layer-cache mechanism, and not something
|
||||
`docker build`/`docker push` (what this workflow actually calls, see
|
||||
below) has any hook into. It doesn't apply to this problem; #1–#3 do.
|
||||
|
||||
## 1. What the workflow and Dockerfile actually do today
|
||||
|
||||
`.gitea/workflows/publish.yml` runs plain `docker build` and `docker push` —
|
||||
not `docker/build-push-action` or `docker buildx build` with any cache flags
|
||||
(lines 86–89, 96/105/109). `runs-on: ubuntu-latest` is a Gitea-side runner
|
||||
*label*, matched against whatever labels this instance's `act_runner` was
|
||||
registered with — Gitea's own runner docs describe labels as exactly this
|
||||
kind of local mapping (`ubuntu-latest:docker://...` style entries a runner
|
||||
operator configures, not a GitHub-hosted VM type)
|
||||
([search result summary of docs.gitea.com/usage/actions, act_runner label
|
||||
docs](https://docs.gitea.com/1.23/usage/actions/act-runner/); the specific
|
||||
`main_runner` name mentioned as a possible identifier for this instance's
|
||||
runner doesn't appear anywhere in this repo's tracked files — it would only
|
||||
show up in the Gitea Actions run UI, not the workflow YAML). The disk-fill
|
||||
problem the "Free up runner disk" step's own comment describes (*"nothing
|
||||
here ever pruned old ones, so the runner's disk fills up over successive
|
||||
runs"*) is only possible on a runner whose disk persists between runs —
|
||||
corroborated independently by act_runner's own architecture: its Docker
|
||||
executor keeps a long-lived host Docker daemon and reuses the image cache
|
||||
across job runs by default (act_runner documentation on the docker/dind
|
||||
executor flavors and idle-cleanup behavior — runner cleans up stale
|
||||
workspaces on an interval rather than starting from a fresh disk each job).
|
||||
|
||||
The `Dockerfile`'s expensive work is one `RUN` chain (lines 19–62): a single
|
||||
`apt-get install` covering `build-essential`, `scons`, `mingw-w64`, and the
|
||||
X11/audio/udev dev headers, immediately followed — in the same layer, via
|
||||
`&&` — by the `curl`+`unzip` of Godot's editor and export templates and the
|
||||
`curl`+`tar` of the Blender tarball. Because it's one `RUN`, it's one cache
|
||||
entry: any change anywhere in it (including the Godot/Blender URLs, which
|
||||
change on essentially every nightly build since the workflow tracks
|
||||
newest-stable) invalidates the whole thing, apt install included.
|
||||
|
||||
## 2. Docker/BuildKit's own caching mechanisms (docs.docker.com)
|
||||
|
||||
- **Layer cache (default, local, free)**: BuildKit caches each instruction's
|
||||
result keyed on that instruction's content plus everything before it in
|
||||
the Dockerfile; a build on the same machine reuses it automatically with
|
||||
no extra flags, provided the cache wasn't evicted
|
||||
([docs.docker.com/build/cache](https://docs.docker.com/build/cache/)).
|
||||
This is what #1+#2 above unlock for this repo — it needs nothing but (a)
|
||||
not deleting it and (b) ordering the Dockerfile so the part that doesn't
|
||||
change (apt) comes before the part that does (downloads).
|
||||
- **`RUN --mount=type=cache`**: a *build cache mount*, separate from the
|
||||
layer cache — a directory that survives across builder invocations
|
||||
without itself being part of the cached layer, intended for package-
|
||||
manager caches like apt's `/var/cache/apt`/`/var/lib/apt`. Needs
|
||||
`sharing=locked` for apt specifically, since apt needs exclusive access to
|
||||
its own state
|
||||
([docs.docker.com/reference/dockerfile/#run---mounttypecache](https://docs.docker.com/reference/dockerfile/#run---mounttypecache)).
|
||||
Persists even when the layer cache above gets invalidated by a Dockerfile
|
||||
edit — a smaller, complementary win, not a replacement for #2's layer
|
||||
split.
|
||||
- **`--cache-to`/`--cache-from type=registry`**: exports/imports build
|
||||
cache to/from an OCI registry, as a separate artifact from the final
|
||||
image, specifically to let machines that don't share local disk share a
|
||||
cache (`mode=max` caches every stage, not just the final one — bigger
|
||||
push, more reuse potential)
|
||||
([docs.docker.com/build/cache/backends/registry](https://docs.docker.com/build/cache/backends/registry/)).
|
||||
Not needed for a single persistent runner (see verdict §4).
|
||||
- **`BUILDKIT_INLINE_CACHE`/`type=inline`**: embeds cache metadata directly
|
||||
in the pushed image manifest instead of a separate cache artifact —
|
||||
Docker's own CI guidance is "in most cases you want to use the inline
|
||||
cache exporter" for simple cases, but it only supports `mode=min` (final
|
||||
stage only) versus registry cache's `min`/`max`
|
||||
([docs.docker.com/build/ci/github-actions/cache](https://docs.docker.com/build/ci/github-actions/cache/)).
|
||||
Same applicability caveat as registry cache: solves a multi-machine
|
||||
problem this repo doesn't have.
|
||||
- Docker Engine ≥23 makes BuildKit (via buildx) the default builder for
|
||||
plain `docker build` — *"Set Buildx and BuildKit as the default builder on
|
||||
Linux. Alias `docker build` to `docker buildx build`"*
|
||||
([docs.docker.com/engine/release-notes/23.0](https://docs.docker.com/engine/release-notes/23.0/)).
|
||||
So the workflow's existing plain `docker build` call (no `buildx` in the
|
||||
command) should already be getting BuildKit's layer cache today — it's
|
||||
the prune step deleting it that matters, not the build command needing to
|
||||
change to buildx.
|
||||
|
||||
## 3. `docker system prune -af --volumes` vs `docker builder prune`
|
||||
|
||||
Confirmed directly against Docker's CLI reference:
|
||||
|
||||
- `docker system prune`'s default removal set is stopped containers, unused
|
||||
networks, dangling images, and **unused build cache**; `-a`/`--all`
|
||||
widens image removal to *all* unused images (not just dangling), and
|
||||
`--volumes` additionally removes unattached anonymous volumes
|
||||
([docs.docker.com/reference/cli/docker/system/prune](https://docs.docker.com/reference/cli/docker/system/prune/)).
|
||||
There's no flag on `system prune` to keep build cache while still pruning
|
||||
everything else — build cache removal isn't optional once you call it.
|
||||
- `docker builder prune` is the narrower, cache-only equivalent: `--all` to
|
||||
remove all unused cache (not just dangling), `--filter until=<duration>`
|
||||
to only remove cache older than a given age, `--keep-storage=<size>` to
|
||||
cap total cache size instead of clearing it outright
|
||||
([docs.docker.com/reference/cli/docker/builder/prune](https://docs.docker.com/reference/cli/docker/builder/prune/)).
|
||||
|
||||
This is the concrete lever for recommendation #1: keep freeing the disk the
|
||||
workflow's own comment says filled up (old images, old anonymous volumes),
|
||||
but do it with `container prune`/`image prune -af`/`volume prune` instead of
|
||||
`system prune`, and bound the build cache separately with
|
||||
`docker builder prune -f --filter until=24h` (or a `--keep-storage` cap)
|
||||
rather than deleting all of it unconditionally before every build. A same-
|
||||
day rerun (e.g. `workflow_dispatch` shortly after the nightly cron) would
|
||||
then still get a cache hit on the apt layer; a multi-day-old apt cache entry
|
||||
still gets swept on the next age-filtered prune, so disk doesn't grow
|
||||
unbounded either.
|
||||
|
||||
## 4. Layer-order restructuring compatibility
|
||||
|
||||
Splitting `Dockerfile`'s one `RUN` into "apt install" then "Godot/Blender
|
||||
downloads" is a pure reorder/split — no semantic change, no new
|
||||
dependencies, and it's exactly what BuildKit's per-instruction cache keying
|
||||
is designed to reward (§2). Its payoff is entirely contingent on §3: with
|
||||
`docker system prune -af --volumes` still running first, splitting the
|
||||
layer changes nothing, because the reusable apt-layer cache entry is deleted
|
||||
moments before the build that would've hit it starts. The two
|
||||
recommendations are a pair, not independent options.
|
||||
|
||||
## 5. Registry cache realism for this specific image
|
||||
|
||||
Weighed directly against this repo's shape:
|
||||
|
||||
- **Storage cost**: `mode=max` registry cache stores every stage as its own
|
||||
cache layer set in the registry, separate from the pushed image
|
||||
([docs.docker.com/build/cache/backends/registry](https://docs.docker.com/build/cache/backends/registry/)) —
|
||||
for an image whose expensive layers are multi-GB Godot export templates
|
||||
and a Blender tarball that both change nearly nightly, that's another
|
||||
near-full copy of those multi-GB layers pushed to Gitea's package registry
|
||||
on top of the image push that already happens, for cache entries with a
|
||||
short effective lifetime (superseded the next time Godot/Blender bump).
|
||||
- **Gitea registry support**: Gitea's container registry is OCI-compliant
|
||||
for standard image/Helm-chart manifests
|
||||
([docs.gitea.com/usage/packages/container](https://docs.gitea.com/usage/packages/container/)),
|
||||
but general OCI-*artifact* support (the category buildx's cache manifest
|
||||
falls into, using non-image media types) only landed in Gitea 1.24 via
|
||||
[go-gitea/gitea#25846](https://github.com/go-gitea/gitea/issues/25846) —
|
||||
prior to that, pushes of non-standard-image manifests failed with
|
||||
`501 unsupported: Schema version is not supported`. This repo's actual
|
||||
Gitea server version wasn't checked as part of this research, so this is
|
||||
a real "verify before use" gate, not just a style preference.
|
||||
- **Time saved**: none of it addresses the local-persistent-runner case
|
||||
this repo actually has — §2/§4's plain layer-cache fix gets the same or
|
||||
better result (apt-layer cache hits) for free, with no push/pull latency
|
||||
and no registry storage growth.
|
||||
|
||||
Net: registry cache isn't wrong in general, it's solving a problem (cache
|
||||
sharing across machines without shared disk) this repo doesn't have, at a
|
||||
storage and reliability cost this repo's layer shapes make worse than
|
||||
typical.
|
||||
|
||||
## Sources
|
||||
|
||||
- `Dockerfile` — this repo's build, current single `RUN` chain (apt install
|
||||
+ Godot download/unpack + Blender download/unpack), fetched at HEAD
|
||||
- `.gitea/workflows/publish.yml` — `runs-on: ubuntu-latest`, plain
|
||||
`docker build`/`docker push` (no buildx/cache flags), the "Free up runner
|
||||
disk" step and its comment explaining *why* it prunes before every build
|
||||
- https://docs.docker.com/build/cache/ — BuildKit layer cache mechanics,
|
||||
invalidation model
|
||||
- https://docs.docker.com/reference/dockerfile/#run---mounttypecache —
|
||||
`RUN --mount=type=cache`, apt cache-mount example, `sharing=locked`
|
||||
- https://docs.docker.com/build/cache/backends/registry/ — `--cache-to`/
|
||||
`--cache-from type=registry`, `mode=min`/`max`
|
||||
- https://docs.docker.com/build/ci/github-actions/cache/ —
|
||||
`BUILDKIT_INLINE_CACHE`/`type=inline` vs registry cache tradeoffs
|
||||
- https://docs.docker.com/engine/release-notes/23.0/ — BuildKit/buildx as
|
||||
default `docker build` builder since Engine 23.0
|
||||
- https://docs.docker.com/reference/cli/docker/system/prune/ — `docker
|
||||
system prune` default removal set (incl. build cache), `-a`/`--volumes`
|
||||
- https://docs.docker.com/reference/cli/docker/builder/prune/ — `docker
|
||||
builder prune`, `--filter until=`, `--keep-storage`
|
||||
- https://docs.gitea.com/usage/actions/act-runner and
|
||||
https://docs.gitea.com/1.23/usage/actions/act-runner/ — act_runner
|
||||
labels, docker/dind executor persistence and idle cleanup behavior
|
||||
- https://about.gitea.com/resources/tutorials/enable-gitea-actions-cache-to-accelerate-cicd/
|
||||
and https://docs.gitea.com/usage/actions/overview/ — Gitea Actions'
|
||||
`actions/cache` support and default-enabled cache server
|
||||
- https://docs.gitea.com/usage/packages/container/ — Gitea's OCI-compliant
|
||||
container registry
|
||||
- https://github.com/go-gitea/gitea/issues/25846 (closed via PR #34666) —
|
||||
Gitea container registry's OCI-artifact (non-image-manifest) support
|
||||
landing in 1.24, and the `501 unsupported: Schema version is not
|
||||
supported` error prior to that
|
||||
- https://gitea.com/gitea/act_runner — act_runner project, docker executor
|
||||
Reference in New Issue
Block a user