323 lines
15 KiB
Markdown
323 lines
15 KiB
Markdown
# vegardit/gitea-act-runner <a href="https://github.com/vegardit/docker-gitea-act-runner/" title="GitHub Repo"><img height="30" src="https://raw.githubusercontent.com/simple-icons/simple-icons/develop/icons/github.svg?sanitize=true"></a>
|
|
|
|
[](https://github.com/vegardit/docker-gitea-act-runner/actions?query=workflow%3ABuild)
|
|
[](#license)
|
|
[](https://hub.docker.com/r/vegardit/gitea-act-runner)
|
|
[](https://hub.docker.com/r/vegardit/gitea-act-runner)
|
|
[](CODE_OF_CONDUCT.md)
|
|
|
|
1. [What is it?](#what-is-it)
|
|
1. [Usage](#usage)
|
|
1. [License](#license)
|
|
|
|
|
|
## <a name="what-is-it"></a>What is it?
|
|
|
|
Multi-arch Docker image based on `debian:stable-slim` containing [Gitea](https://gitea.com)'s [act_runner](https://gitea.com/gitea/act_runner/)
|
|
|
|
#### Why not using Alpine Linux?
|
|
- musl-libc - Alpine's Greatest Weakness https://www.linkedin.com/pulse/musl-libc-alpines-greatest-weakness-rogan-lynch
|
|
- Why I will never use Alpine Linux ever again https://martinheinz.dev/blog/92
|
|
- Does Alpine have known DNS issue within Kubernetes? https://stackoverflow.com/questions/65181012/
|
|
- Why is the Alpine Docker image over 50% slower than the Ubuntu image? https://superuser.com/questions/1219609/
|
|
- Performance issue with alpine musl library https://unix.stackexchange.com/questions/729342/
|
|
|
|
|
|
## <a name="usage"></a>Usage
|
|
|
|
The docker image comes in three flavors:
|
|
- `vegardit/gitea-act-runner:latest`: only contains the Gitea act_runner and executes workflows in containers of the docker engine running act_runner itself (DooD / Docker-out-of-Docker approach)
|
|
- `vegardit/gitea-act-runner:dind-latest`: executes workflows using an embedded docker engine (DinD / Docker-in-Docker approach) providing better process isolation
|
|
- `vegardit/gitea-act-runner:dind-rootless-latest`: executes workflows using an embedded docker engine (DinD / Docker-in-Docker approach) running the docker daemon as a non-root user [(Rootless mode)](https://docs.docker.com/engine/security/rootless/)
|
|
|
|
### Docker Run
|
|
|
|
Running from the command line:
|
|
|
|
- Docker-out-of-Docker approach
|
|
```py
|
|
docker run \
|
|
-e GITEA_INSTANCE_URL=https://gitea.example.com \
|
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<INSERT_TOKEN_HERE> \
|
|
--name gitea_act_runner \
|
|
-v /var/run/docker.sock:/var/run/docker.sock:rw \
|
|
vegardit/gitea-act-runner:latest
|
|
```
|
|
|
|
- Docker-in-Docker approach
|
|
```py
|
|
docker run \
|
|
-e GITEA_INSTANCE_URL=https://gitea.example.com \
|
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<INSERT_TOKEN_HERE> \
|
|
--name gitea_act_runner \
|
|
--privileged \
|
|
vegardit/gitea-act-runner:dind-latest
|
|
```
|
|
|
|
- Docker-in-Docker approach with Docker daemon running as a non-root user (Rootless mode)
|
|
```py
|
|
docker run \
|
|
-e GITEA_INSTANCE_URL=https://gitea.example.com \
|
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<INSERT_TOKEN_HERE> \
|
|
--name gitea_act_runner \
|
|
--security-opt seccomp=unconfined \
|
|
--security-opt apparmor=unconfined \
|
|
--security-opt systempaths=unconfined \
|
|
vegardit/gitea-act-runner:dind-rootless-latest
|
|
```
|
|
|
|
### Docker Compose
|
|
|
|
Example `docker-compose.yml`:
|
|
|
|
- Docker-out-of-Docker approach
|
|
```yaml
|
|
# https://docs.docker.com/compose/compose-file/
|
|
|
|
services:
|
|
|
|
gitea_act_runner:
|
|
image: vegardit/gitea-act-runner:latest
|
|
#image: ghcr.io/vegardit/gitea-act-runner:latest
|
|
restart: always
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:rw
|
|
- /my/path/to/data/dir:/data:rw # the config file is located at /data/.runner and needs to survive container restarts
|
|
environment:
|
|
TZ: "Europe/Berlin"
|
|
# config parameters for initial runner registration:
|
|
GITEA_INSTANCE_URL: 'https://gitea.example.com' # required
|
|
GITEA_RUNNER_REGISTRATION_TOKEN_FILE: 'path/to/file' # one-time registration token, only required on first container start
|
|
# or: GITEA_RUNNER_REGISTRATION_TOKEN: '<INSERT_TOKEN_HERE>'
|
|
```
|
|
|
|
- Docker-in-Docker approach
|
|
```yaml
|
|
# https://docs.docker.com/compose/compose-file/
|
|
|
|
services:
|
|
|
|
gitea_act_runner:
|
|
image: vegardit/gitea-act-runner:dind-latest
|
|
#image: ghcr.io/vegardit/gitea-act-runner:dind-latest
|
|
privileged: true
|
|
restart: always
|
|
volumes:
|
|
- /my/path/to/data/dir:/data:rw # the config file is located at /data/.runner and needs to survive container restarts
|
|
environment:
|
|
TZ: "Europe/Berlin"
|
|
# config parameters for initial runner registration:
|
|
GITEA_INSTANCE_URL: 'https://gitea.example.com' # required
|
|
GITEA_RUNNER_REGISTRATION_TOKEN_FILE: 'path/to/file' # one-time registration token, only required on first container start
|
|
# or: GITEA_RUNNER_REGISTRATION_TOKEN: '<INSERT_TOKEN_HERE>'
|
|
```
|
|
|
|
- Docker-in-Docker rootless approach
|
|
```yaml
|
|
# https://docs.docker.com/compose/compose-file/
|
|
|
|
services:
|
|
|
|
gitea_act_runner:
|
|
image: vegardit/gitea-act-runner:dind-rootless-latest
|
|
#image: ghcr.io/vegardit/gitea-act-runner:dind-rootless-latest
|
|
restart: always
|
|
security_opt:
|
|
- seccomp:unconfined
|
|
- apparmor:unconfined
|
|
- systempaths=unconfined
|
|
volumes:
|
|
- /my/path/to/data/dir:/data:rw # the config file is located at /data/.runner and needs to survive container restarts
|
|
environment:
|
|
TZ: "Europe/Berlin"
|
|
# config parameters for initial runner registration:
|
|
GITEA_INSTANCE_URL: 'https://gitea.example.com' # required
|
|
GITEA_RUNNER_REGISTRATION_TOKEN_FILE: 'path/to/file' # one-time registration token, only required on first container start
|
|
# or: GITEA_RUNNER_REGISTRATION_TOKEN: '<INSERT_TOKEN_HERE>'
|
|
```
|
|
|
|
### Kubernetes Deployment
|
|
|
|
Example deployment for Kubernetes:
|
|
|
|
- Docker-out-of-Docker approach (requires access to host Docker socket)
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: gitea-act-runner
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: gitea-act-runner
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: gitea-act-runner
|
|
spec:
|
|
containers:
|
|
- name: runner
|
|
image: vegardit/gitea-act-runner:latest
|
|
env:
|
|
- name: GITEA_INSTANCE_URL
|
|
value: "https://gitea.example.com"
|
|
- name: GITEA_RUNNER_REGISTRATION_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gitea-runner-secret
|
|
key: registration-token
|
|
volumeMounts:
|
|
- name: docker-sock
|
|
mountPath: /var/run/docker.sock
|
|
- name: data
|
|
mountPath: /data
|
|
volumes:
|
|
- name: docker-sock
|
|
hostPath:
|
|
path: /var/run/docker.sock
|
|
type: Socket
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: gitea-runner-pvc
|
|
---
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: gitea-runner-secret
|
|
type: Opaque
|
|
stringData:
|
|
registration-token: "<INSERT_TOKEN_HERE>"
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: gitea-runner-pvc
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
resources:
|
|
requests:
|
|
storage: 10Gi
|
|
```
|
|
|
|
- Docker-in-Docker approach (more secure, doesn't require host Docker access)
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: gitea-act-runner-dind
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: gitea-act-runner-dind
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: gitea-act-runner-dind
|
|
spec:
|
|
containers:
|
|
- name: runner
|
|
image: vegardit/gitea-act-runner:dind-latest
|
|
securityContext:
|
|
privileged: true
|
|
env:
|
|
- name: GITEA_INSTANCE_URL
|
|
value: "https://gitea.example.com"
|
|
- name: GITEA_RUNNER_REGISTRATION_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gitea-runner-secret
|
|
key: registration-token
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /data
|
|
- name: docker-storage
|
|
mountPath: /var/lib/docker
|
|
volumes:
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: gitea-runner-data-pvc
|
|
- name: docker-storage
|
|
emptyDir: {}
|
|
```
|
|
|
|
**Notes for Kubernetes deployments:**
|
|
- For production, prefer the **DinD (rootful)** approach over DooD because it avoids exposing the host Docker socket and provides better isolation from the host.
|
|
- **DinD-rootless** is generally **not recommended** in Kubernetes because:
|
|
- It often requires permissive settings (`seccompProfile: Unconfined`, AppArmor `unconfined`) that many clusters disallow under their Pod Security Standards.
|
|
- It provides limited additional security at the pod boundary compared to rootful DinD, while still needing relaxed sandboxing.
|
|
- It has practical limitations (user-mode networking via `slirp4netns`, port publishing quirks, performance trade-offs, storage driver constraints such as `fuse-overlayfs`).
|
|
- If you need "rootless" semantics, consider using **native Kubernetes Jobs** (or a Kubernetes-native runner/executor) instead of running a Docker daemon inside the pod.
|
|
|
|
### Additional environment variables
|
|
|
|
The following environment variables can be specified to further configure the service.
|
|
|
|
#### Runner registration:
|
|
Name|Default Value|Description
|
|
----|-------------|-----------
|
|
GITEA_INSTANCE_INSECURE|`false`|If `true` don't verify the TLS certificate of the Gitea instance
|
|
GITEA_RUNNER_NAME|`<empty>`|If not specified the container's hostname is used
|
|
GITEA_RUNNER_EPHEMERAL|`false`|If `true` or `1`, the runner will exit after completing a single job
|
|
GITEA_RUNNER_REGISTRATION_FILE|`/data/.runner`|The JSON file that holds the result from the runner registration with the Gitea instance
|
|
GITEA_RUNNER_REGISTRATION_TIMEOUT|`30`|In case of failure, registration is retried until this timeout in seconds is reached
|
|
GITEA_RUNNER_REGISTRATION_RETRY_INTERVAL|`5`|Wait period in seconds between registration retries
|
|
|
|
#### Runner runtime config:
|
|
|
|
Name|Default Value|Description
|
|
----|-------------|-----------
|
|
GITEA_RUNNER_CONFIG_TEMPLATE_FILE|`/opt/config.template.yaml`|Template to derive the effective config file from, see [image/config.template.yaml](image/config.template.yaml)
|
|
GITEA_RUNNER_UID|`1000`|The UID of the Gitea runner process
|
|
GITEA_RUNNER_GID|`1000`|The GID of the Gitea runner process
|
|
GITEA_RUNNER_LOG_EFFECTIVE_CONFIG|`false`|If set to true logs the effective YAML configuration to stdout during startup.
|
|
|
|
#### Runner config template variables
|
|
|
|
The following environment variables are referenced in the `/opt/config.template.yaml` file.
|
|
|
|
Name|Default Value|Description
|
|
----|-------------|-----------
|
|
GITEA_RUNNER_LABELS|`<empty>`|Comma-separated list of labels in the format of `label[:schema[:args]]`.<br>If not specified the following labels are used<ol><li>`ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:act-latest`<li>`ubuntu-24.04:docker://ghcr.io/catthehacker/ubuntu:act-24.04`<li>`ubuntu-22.04:docker://ghcr.io/catthehacker/ubuntu:act-22.04`<li>`ubuntu-20.04:docker://ghcr.io/catthehacker/ubuntu:act-20.04`</ol>
|
|
GITEA_RUNNER_LOG_LEVEL|`info`|The level of logging, can be trace, debug, info, warn, error, fatal
|
|
GITEA_RUNNER_ENV_FILE|`/data/.env`|Extra environment variables to run jobs from a file
|
|
GITEA_RUNNER_FETCH_TIMEOUT|`5s`|The timeout for fetching the job from the Gitea instance
|
|
GITEA_RUNNER_FETCH_INTERVAL|`2s`|The interval for fetching the job from the Gitea instance
|
|
GITEA_RUNNER_GITHUB_MIRROR|`<empty>`| Base URL to use instead of https://github.com when fetching GitHub Actions.
|
|
GITEA_RUNNER_MAX_PARALLEL_JOBS|`1`|Maximum number of concurrently executed jobs
|
|
GITEA_RUNNER_JOB_CONTAINER_DOCKER_HOST|`<empty>`|If empty, the available docker host is located automatically. If set to `-`, the available docker host is located automatically, but the docker host won't be mounted to the job containers. If it's any other value, the specified docker host will be used.
|
|
GITEA_RUNNER_JOB_CONTAINER_NETWORK|`bridge`|Docker network to use with job containers. Can be `bridge`, `host`, `none`, or the name of a custom network
|
|
GITEA_RUNNER_JOB_CONTAINER_PRIVILEGED|`false`|Whether to run jobs in containers with privileged mode which is required for **Docker-in-Docker** aka **dind**
|
|
GITEA_RUNNER_JOB_CONTAINER_OPTIONS|`<empty>`|Additional container launch options (eg, --add-host=my.gitea.url:host-gateway)
|
|
GITEA_RUNNER_JOB_CONTAINER_WORKDIR_PARENT|`/workspace`|The parent directory of a job's working directory
|
|
GITEA_RUNNER_JOB_CONTAINER_FORCE_PULL|`true`|Pull docker image(s) even if already present
|
|
GITEA_RUNNER_JOB_CONTAINER_FORCE_REBUILD|`false`|Rebuild docker image(s) even if already present
|
|
GITEA_RUNNER_JOB_TIMEOUT|`3h`|The maximum time a job can run before it is cancelled
|
|
GITEA_RUNNER_SHUTDOWN_TIMEOUT|`0s`|The timeout for the runner to wait for running jobs to finish when shutting down
|
|
GITEA_RUNNER_ENV_VAR_**N**_NAME|`<empty>`|Name of the **N**-th extra environment variable to be passed to Job containers, e.g. `GITEA_RUNNER_ENV_VAR_1_NAME=MY_AUTH_TOKEN`
|
|
GITEA_RUNNER_ENV_VAR_**N**_VALUE|`<empty>`|Value of the **N**-th extra environment variable to be passed to Job containers, e.g. `GITEA_RUNNER_ENV_VAR_1_VALUE=SGVsbG8gbXkgZnJpZW5kIQ==`
|
|
GITEA_RUNNER_VALID_VOLUME_**N**|`<empty>`|Volumes (including bind mounts) that are allowed to be mounted into job containers. [Glob syntax](https://github.com/gobwas/glob) is supported, e.g. `GITEA_RUNNER_VALID_VOLUME_1=/src/*.json`
|
|
GITEA_RUNNER_HOST_WORKDIR_PARENT|`/data/cache/actions`|The parent directory of a job's working directory. (Path to cache cloned actions)
|
|
|
|
#### Embedded cache server:
|
|
Name|Default Value|Description
|
|
----|-------------|-----------
|
|
ACT_CACHE_SERVER_ENABLED|`true`| Enable the use of an embedded or external cache server with `actions/cache` in jobs
|
|
ACT_CACHE_SERVER_EXTERNAL_URL|`<empty>`|URL to an external cache server. If specified, act_runner will use this URL as the ACTIONS_CACHE_URL instead of starting an embedded server. The URL should end with "/".
|
|
ACT_CACHE_SERVER_DIR|`/data/cache/server`| The directory to store the cache data
|
|
ACT_CACHE_SERVER_HOST|`<empty>`| The IP address or hostname via which the job containers can reach the cache server. Leave empty for automatic detection
|
|
ACT_CACHE_SERVER_PORT|`0`|The TCP port of the cache server. `0` means to use a random, available port
|
|
|
|
## <a name="license"></a>License
|
|
|
|
All files in this repository are released under the [Apache License 2.0](LICENSE.txt).
|
|
|
|
Individual files contain the following tag instead of the full license text:
|
|
```
|
|
SPDX-License-Identifier: Apache-2.0
|
|
```
|
|
|
|
This enables machine processing of license information based on the SPDX License Identifiers that are available here: https://spdx.org/licenses/.
|