Skip to content
Try Gitea Cloud ☁️ for 30 days → Accelerate your Development & Deploys!
Runnerrelease

Gitea Runner 4.0.0 is released

Banner for blog post with title "Gitea Runner 4.0.0 is released"

We are happy to announce the release of Gitea Runner 4.0.0.

This is a runner-side release. It remains wire-compatible with existing Gitea versions; the major version bump reflects two breaking changes rather than a new server dependency.

The runner now ships native Go actions selected with uses: builtin:<name>. Jobs need no action download and no Node in their image (#871):

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: builtin:checkout
- run: make test

The first built-in is builtin:checkout, a subset of actions/checkout with repository, ref, token, path, and fetch-depth. Other inputs fail the step. The runner clones shallowly on its host and copies the tree into the job; gitea-runner exec copies the local working directory for the workflow’s own ref.

Remote actions such as actions/checkout are unchanged.

Runners can store the actions cache in an S3-compatible bucket — AWS S3, MinIO, RustFS, and the like — with no separate cache-server (#1244):

cache:
s3:
endpoint: "http://minio:9000"
bucket: "runner-cache"
prefix: "gitea-runner-cache"
path_style: true
access_key_file: "/run/secrets/cache-s3-access-key"
secret_key_file: "/run/secrets/cache-s3-secret-key"

Runners with the same settings share one cache pool. A cache saved on one runner is visible to the others within about 30 seconds. cache.s3 cannot be combined with cache.external_server; put lifecycle rules on the bucket to expire old caches and abort incomplete multipart uploads.

Operators can send a trace of every job to an OTLP collector. Each job becomes a trace with a span per step, configured through the standard OTEL_* environment variables and off until an endpoint is set (#1206):

Terminal window
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

Jobs receive only TRACEPARENT, so instrumented tools inside them can join the trace without seeing the collector’s credentials. Metrics stay on the Prometheus endpoint. See docs/telemetry.md for setup and security notes.

uses: self:{owner}/{repo}[/{path}]@{ref} resolves on the runner’s Gitea instance, so an action can refer to another repository on the same instance without hard-coding the hostname (#1233):

- uses: self:org/shared-actions/setup@v1

This ships with actionslib v1.2.x: strategy.job-index and strategy.job-total come from Gitea when it sends them, and container, services, and with are evaluated once instead of on every use.

A job whose if: fails to evaluate — for example fromJSON of an invalid job output — is now skipped with the error in its log instead of failing the run, matching GitHub Actions (#1247). Workflows the runner reads itself, such as with exec, are rejected when an expression does not parse.

container.options also accepts --umask (#1238). actions/cache inside composite actions no longer saves caches from failed jobs, the dind images silence the docker-init subreaper warning, and Podman runners in a container get a real Docker socket instead of an empty directory (#1242, #1241, #1231).

1. Cache requests go through the runner (#1229). Jobs no longer talk to cache.external_server or the built-in cache server directly; they send cache requests to their runner, which forwards them. Only runners need to reach cache.external_server, and that server no longer needs to reach Gitea. If jobs can reach an external cache but not their runner — typical with a remote DOCKER_HOST — set cache.host and cache.port to an address those jobs can reach. A runner container on the jobs’ Docker daemon joins each job network automatically, so those jobs can reach its cache without cache.host. Jobs on container.network: bridge cannot reach a runner outside bridge; leave container.network empty or name a user-defined network. The built-in cache has always needed job-to-runner reachability, so both modes now agree.

2. valid_volumes uses doublestar matching (#1214). valid_volumes patterns now match with doublestar instead of gobwas/glob, which was compiled without separators so * crossed slashes. * stays within one path segment and host paths use the OS separator, which also fixes Windows patterns where a backslash was read as an escape. A host pattern that relied on * matching nested paths needs **, for example /data/** instead of /data/*. Named volumes and the bare ** are unaffected.

  • Replace gitea/runner:3.x with gitea/runner:4.0.0 in Docker Compose, Kubernetes manifests, and service units.
  • If jobs use a remote Docker daemon or container.network: bridge, review cache reachability before rolling out (breaking change 1).
  • Audit valid_volumes entries that use * for nested host paths and switch them to ** where needed (breaking change 2).
  • Optional: set cache.s3 to share a cache across runners without a cache-server, try builtin:checkout on images without Node, or set OTEL_EXPORTER_OTLP_ENDPOINT if you want job traces in your collector.
  • Test artifact upload/download, cache restore, and a job on your usual network mode in a staging environment before rolling out broadly.

Pre-built binaries are available from the Gitea Runner downloads page.

The release is also available from the Gitea Runner release page.

Thank you to everyone who contributed code, testing, bug reports, documentation, and feedback since the 3.0.0 release.


  • BREAKING

    • Serve the actions cache from the runner; jobs no longer reach the cache server directly (#1229)
    • Match valid_volumes with doublestar so * stays within one path segment (#1214)
  • FEATURES

    • Add built-in actions framework with builtin:checkout (#871)
    • Support S3-compatible storage as a shared cache backend with cache.s3 (#1244)
    • Add OpenTelemetry tracing via standard OTEL_* variables (#1206)
    • Resolve self:{owner}/{repo} action refs on the runner’s Gitea instance (#1233)
    • Support --umask in container.options (#1238)
  • BUGFIXES

    • Skip jobs whose if: fails to evaluate, matching GitHub Actions (#1247)
    • Stop actions/cache inside composite actions from saving caches of failed jobs (#1242)
    • Give Podman runners in a container a Docker socket instead of an empty directory (#1231)
    • Silence the docker-init subreaper warning in the dind images (#1241)
    • Fix Windows paths in valid_volumes patterns (#1214)
  • FEATURES

    • Let containers started through the job’s Docker socket bind-mount job paths like $PWD without bind_workdir (#1226)
    • Support whole-value ${{ }} in exec for strategy, env, with, services, and outputs (#1221)
  • BUGFIXES

    • Mount the workspace above the repository, fixing pnpm EXDEV / EBUSY (#1224)
    • Upload artifacts to Gitea directly when jobs cannot reach the cache server (#1225)
    • Speed up job setup/teardown and cache requests (#1218, #1222)
  • BUGFIXES
    • Fix IPv6 URLs, cache-server SIGTERM, and atomic task admission (#1217)
    • Stop artifact uploads depending on the cache server reaching Gitea (#1216)
  • BUGFIXES
    • Stop the job’s Docker socket from becoming a directory (#1215)
  • FEATURES

    • Add GITEA_DOCKER_WORKSPACE for compose binds (#1204)
  • BUGFIXES

    • Clean up containers, networks, and volumes a job created when it ends (#1204)
    • Fail the step or job whose expression cannot be interpolated (#1199)
    • Speed up action downloads (#1209)
    • Update golang.org/x/crypto (CVE-2026-78662, CVE-2026-56855) (#1205)
  • FEATURES

    • Add exec --input / --input-file for workflow_dispatch and workflow_call inputs (#1173)
  • BUGFIXES

    • Refuse NODE_OPTIONS from workflows, matching GitHub (#1194)
    • Register every encoded spelling of a secret (#1194)
    • Preserve symlinked Node action entrypoints (#1202)
  • BUGFIXES
    • Keep the runner’s own container.options when privileged mode is off (#1151)
    • Mask secrets on every path they leave a job (#1188)
    • Keep a step’s own with: values out of its inputs context (#1192)
    • Honor volumes declared on service containers (#1186)
    • Report step log ranges with the log flush (#1189)
    • Fail the run when matrix expansion fails (#1187)
  • FEATURES

    • Add log.job.dir for on-disk copies of task logs (#1165)
    • Add runner.default_image for jobs matching no label (#1164)
  • BUGFIXES

    • Stop a failed step disabling the toolkit patch (#1177)
  • FEATURES

    • Add size-based cache eviction and last-access retention (#1170)
    • Support GitHub’s $/ self-repository prefix in uses: (#1150)
    • Default runner.tool_cache_mode to none (shared tool cache is opt-in) (#1171)
  • BUGFIXES

    • Bound every RPC to Gitea and stop failing silently (#1174)
    • Build cache job URLs on the address its runner registered (#1153)
  • FEATURES

    • Wait for healthy services and fill the job context (#1107)
    • Add config command to edit config files (#1140)
    • Gate ::set-env:: / ::add-path:: behind an opt-in (#1109)
  • BUGFIXES

    • Evaluate each ${{ }} part on its own (#1146)
    • Trim whitespace from register inputs (#1147)
  • BUGFIXES
    • Serve the whole results service from the cache server (#1141)
  • BUGFIXES
    • Coerce every expression value kind to a string and drop the format() panic (#1135)