- publish-image.sh: add REGISTRY_NAMESPACE for Gitea Container Registry path - sample workflows: run docker login/build/push on host label (bundled docker CLI + mounted socket); fix doubled image path in build/push sample - docs: record v0.1.0 publish and remaining secret-gated push validation Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
custom-gitea-runner
A versioned, Docker-first, self-owned Gitea Actions runner platform for Unraid and Linux hosts.
Overview • Goals • Architecture • Repository-layout • Quick-start • Environment • Roadmap • Operations
Overview
custom-gitea-runner is a controlled replacement for opaque runner setups that are difficult to debug, mutate at startup, or break unexpectedly when upstream images or runtime assumptions change.
The project packages a Gitea-compatible runner into a predictable Docker image with pinned dependencies, explicit startup logic, clean documentation, and testable operational workflows. The goal is to make the runner understandable, inspectable, and easy to version over time.
This repository is designed for self-hosted environments where the operator wants direct control over:
- base image selection,
- bundled tooling,
- startup and registration flow,
- Docker socket/build behavior,
- release/version policy,
- troubleshooting and rollback.
Why this exists
This project exists to eliminate common self-hosted runner pain points such as:
- missing runtime dependencies like Node.js, Git, or Docker CLI,
- fragile startup behavior that depends on mutable package installs,
- difficult-to-diagnose nested container failures,
- unclear registration/config persistence,
- and weak operational visibility when jobs fail early.
Instead of treating the runner like a disposable black box, this repository treats it like an internal platform component.
Goals
Primary goals
- Build a custom Docker image for a Gitea-compatible runner.
- Bundle required CI dependencies at image build time.
- Support checkout, Node-based actions, Docker login, Docker build, and Docker push workflows.
- Provide deterministic bootstrap and registration behavior.
- Provide clean deployment guidance for Unraid and standard Linux Docker hosts.
- Keep all operational knowledge versioned in-repo.
Secondary goals
- Support custom CA trust.
- Support opinionated label templates.
- Support smoke testing and regression fixtures.
- Support future hardening tracks like rootless or DinD variants.
Non-goals
- Replacing Gitea itself.
- Building a full control plane or autoscaling runner manager in v1.
- Solving every orchestration model on day one.
Architecture
Design principles
- Deterministic image builds.
- No hidden runtime package installs in normal operation.
- Explicit entrypoint and healthcheck behavior.
- Pinned versions where practical.
- Clear separation of image build, registration, runtime config, and operations.
- Logs that are useful during failure, not just success.
Planned v1 stack
- Debian slim or Ubuntu slim base image.
- Pinned
act_runnerbinary or pinned upstream release artifact. - Bundled tooling:
bashcurlgitjqnodenpmdockerca-certificatestini
- Entrypoint script for validation, registration, and daemon startup.
- Healthcheck script for runtime verification.
- Example config and compose files.
Runtime model
The runner container is intended to be long-lived and operator-managed.
Preferred v1 modes:
- Pre-generated config mounted into the container.
- Environment-driven one-time bootstrap registration when config is absent.
For Docker build/push workflows, v1 assumes a trusted host and supports a mounted Docker socket. Future variants can harden or isolate this behavior.
Repository layout
custom-gitea-runner/
├── AGENT.md
├── README.md
├── ROADMAP.md
├── CHANGELOG.md
├── LICENSE
├── .gitignore
├── .env.example
├── docker/
│ ├── runner/
│ │ ├── Dockerfile
│ │ ├── docker-entrypoint.sh
│ │ ├── healthcheck.sh
│ │ └── smoke-test.sh
│ └── compose/
│ ├── docker-compose.example.yml
│ └── unraid-template-notes.md
├── config/
│ ├── runner.example.yaml
│ ├── labels.example.env
│ └── trusted-ca/
├── scripts/
│ ├── register-runner.sh
│ ├── deregister-runner.sh
│ ├── verify-runtime.sh
│ ├── build-image.sh
│ ├── publish-image.sh
│ └── test-local-runner.sh
├── docs/
│ ├── architecture.md
│ ├── operations.md
│ ├── troubleshooting.md
│ ├── unraid.md
│ └── decisions/
│ └── 0001-base-image.md
├── test/
│ ├── fixtures/
│ ├── sample-workflows/
│ └── smoke/
└── src/
└── optional/
Quick start
1. Create the repository
Create a new Gitea repository named custom-gitea-runner and commit the initial documentation set:
AGENT.mdREADME.mdROADMAP.mdCHANGELOG.md.gitignore.env.example
2. Build the first image
Once the Dockerfile exists:
docker build -t custom-gitea-runner:v0.1.0 -f docker/runner/Dockerfile .
3. Provide runtime configuration
Create a .env file from .env.example and decide whether to:
- mount a pre-generated runner config, or
- let the container self-register on first boot.
4. Start the runner
Use Docker Compose or the equivalent Unraid template settings to start the container with:
- persistent config mount,
- persistent work directory,
- Docker socket mount if build/push is required,
- required env vars.
5. Run smoke workflows
Before production use, validate at minimum:
- checkout-only workflow,
- Node-based action workflow,
- Docker login workflow,
- Docker build/push workflow.
Environment
Planned environment variables for bootstrap mode:
| Variable | Required | Purpose |
|---|---|---|
GITEA_INSTANCE_URL |
Yes | Base URL of the Gitea instance. |
GITEA_RUNNER_TOKEN |
Yes | Registration token for the runner. |
GITEA_RUNNER_NAME |
Yes | Human-readable runner name. |
GITEA_RUNNER_LABELS |
No | Comma-separated label list. |
GITEA_RUNNER_WORKDIR |
No | Working directory inside the container. |
GITEA_RUNNER_CONFIG_PATH |
No | Path to the runner config file. |
TZ |
No | Time zone for logs and runtime behavior. |
Future environment variables may include:
- custom CA paths,
- logging mode,
- healthcheck tuning,
- Docker socket override,
- feature flags for experimental runner behavior.
Example deployment model
Docker Compose direction
The project will include a compose example that follows this pattern:
- pinned image tag,
- mounted runner config,
- mounted work directory,
- mounted Docker socket,
- explicit restart policy,
- explicit environment variables.
Example shape:
services:
gitea-runner:
image: custom-gitea-runner:v0.1.0
container_name: custom-gitea-runner
restart: unless-stopped
environment:
GITEA_INSTANCE_URL: https://git.example.com
GITEA_RUNNER_NAME: unraid-runner-01
GITEA_RUNNER_TOKEN: ${GITEA_RUNNER_TOKEN}
GITEA_RUNNER_LABELS: ubuntu-latest,docker
TZ: America/Chicago
volumes:
- ./config:/config
- ./work:/work
- /var/run/docker.sock:/var/run/docker.sock
Unraid direction
The Unraid notes should document:
- repository/image field,
- appdata path recommendations,
- variable definitions,
- volume mounts,
- Docker socket usage,
- update workflow,
- rollback workflow.
Operations
Expected startup behavior
At startup, the container should log:
- image version,
- embedded git revision if available,
act_runnerversion,nodeversion,dockerversion,- config path,
- work path,
- registration mode,
- target Gitea URL,
- active labels.
Health behavior
The healthcheck should confirm:
- runner process is alive,
- config exists,
- required tools exist,
- Docker socket is available when configured.
Upgrade workflow
Recommended upgrade process:
- Pull or build a new pinned image tag.
- Review
CHANGELOG.md. - Stop the existing container.
- Update image tag in compose or Unraid template.
- Start the container.
- Verify logs and healthcheck.
- Run smoke workflow.
Rollback workflow
- Stop the container.
- Revert to prior known-good image tag.
- Start the container.
- Verify registration/config still valid.
- Confirm smoke workflow success.
Documentation set
This repository should maintain the following docs as first-class assets:
AGENT.md— build rules, architecture constraints, agent instructions.README.md— project overview, install/use guidance, operational entry point.ROADMAP.md— phased plan with active status tracking.CHANGELOG.md— release history.docs/architecture.md— deeper design details.docs/operations.md— runtime/admin guidance.docs/troubleshooting.md— symptom-driven fixes.docs/unraid.md— Unraid-specific deployment instructions.docs/decisions/*.md— ADR-style architecture decisions.
Testing strategy
Required validation layers:
- image build verification,
- runtime tool verification,
- registration smoke tests,
- workflow smoke tests,
- regression tests for previously encountered failure classes.
The first workflows to validate should be:
actions/checkout- Node-based action execution
docker/login-actiondocker builddocker push
Roadmap
Project planning lives in ROADMAP.md.
High-level phases:
- Phase 0: Research and design
- Phase 1: Base image and bootstrap
- Phase 2: Registration/config flows
- Phase 3: Docker workflow support
- Phase 4: Test harness and smoke validation
- Phase 5: Unraid packaging and docs
- Phase 6: Hardening and enhancements
Release model
Recommended versioning:
v0.1.0v0.1.1v0.2.0
latest may exist as a convenience tag, but documentation should always recommend pinned versions for real deployments.
Every release should map to:
- a Git tag,
- a CHANGELOG entry,
- image metadata labels,
- and documented upgrade notes where relevant.
Contributing workflow
Recommended development loop:
- Update docs first when requirements or behavior change.
- Make small, reviewable changes.
- Keep scripts simple and auditable.
- Add regression coverage for painful bugs.
- Prefer explicit configuration over implicit behavior.
Current status
This repository is in planning/bootstrap stage.
The immediate next steps are:
- finalize the architecture direction,
- scaffold the repository,
- build the first pinned image,
- implement registration/bootstrap scripts,
- validate smoke workflows,
- document Unraid deployment.