mirror of
https://gitea.com/gitea/docs.git
synced 2026-09-17 19:55:34 +00:00
Reviewed-on: https://gitea.com/gitea/docs/pulls/488 Reviewed-by: silverwind <[email protected]>
148 lines
6.8 KiB
Markdown
148 lines
6.8 KiB
Markdown
---
|
|
sidebar_position: 2
|
|
---
|
|
|
|
# Install with Docker
|
|
|
|
The official images are published on [Docker Hub](https://hub.docker.com/r/gitea/runner/tags) as `docker.io/gitea/runner`.
|
|
`latest` is the newest release, `nightly` is built from the `main` branch, and every release is also tagged with its version.
|
|
|
|
In the container the registration and the daemon are combined: the entrypoint registers the runner on first start (when no registration file exists yet) and then execs `gitea-runner daemon`.
|
|
|
|
## Image flavours
|
|
|
|
All flavours contain the same `gitea-runner` binary and differ only in how a Docker daemon is made available to jobs.
|
|
|
|
| Tag | Base image | Docker daemon | Supervisor | Runs as |
|
|
| --- | --- | --- | --- | --- |
|
|
| `nightly`, `latest`, `<version>` | `alpine` | none, you provide one | `tini` | `root` |
|
|
| `nightly-dind`, `latest-dind` | `docker:dind` | bundled, needs `--privileged` | `s6` | `root` |
|
|
| `nightly-dind-rootless`, `latest-dind-rootless` | `docker:dind-rootless` | bundled, rootless | `s6` | `rootless` (UID 1000) |
|
|
|
|
The rootless flavour's UID is fixed at 1000 by the upstream base image, and its daemon always listens on `/run/user/1000/docker.sock`, so `--user 1001` does not work. To talk to a *host* rootless daemon under another UID, use the basic flavour and bind-mount that daemon's socket instead.
|
|
|
|
## Basic flavour
|
|
|
|
The default image ships no daemon of its own, so jobs that use `docker://` images need one from outside the container — usually the host's socket:
|
|
|
|
```bash
|
|
docker run -d --name my_runner \
|
|
-e GITEA_INSTANCE_URL=<instance_url> \
|
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<registration_token> \
|
|
-e GITEA_RUNNER_NAME=<runner_name> \
|
|
-v $PWD/data:/data \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
docker.io/gitea/runner:nightly
|
|
```
|
|
|
|
This flavour does not need `--privileged`. The trade-off is that jobs share the host's daemon and can therefore see its other containers and images. A job that can reach the socket can also read the reusable `GITEA_RUNNER_REGISTRATION_TOKEN` from the runner container's `docker inspect` output.
|
|
|
|
## Docker-in-Docker
|
|
|
|
The `dind` flavour bundles its own daemon, so no socket has to be mounted:
|
|
|
|
```bash
|
|
docker run -d --name my_runner --privileged \
|
|
-e GITEA_INSTANCE_URL=<instance_url> \
|
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<registration_token> \
|
|
-v $PWD/data:/data \
|
|
docker.io/gitea/runner:nightly-dind
|
|
```
|
|
|
|
`s6` starts `dockerd` first and the runner service waits for it before registering. Use `nightly-dind-rootless` to run both the daemon and the runner as an unprivileged user; rootless Docker's usual limitations around networking, cgroups and storage drivers apply.
|
|
|
|
## Volumes
|
|
|
|
Two different pieces of state are worth persisting, and neither implies the other:
|
|
|
|
- `/data` is the runner's working directory. It holds the `.runner` registration file and, optionally, the config file. Without it, a recreated container registers itself again as a new runner, leaving a stale entry in Gitea, and fails outright if the token has been reset in the meantime.
|
|
- the Docker daemon's data root holds the images pulled for jobs. It is **not** under `/data`: for `dind` it is `/var/lib/docker` inside the container, for `dind-rootless` it is `/home/rootless/.local/share/docker`. Give it its own volume, or every new container re-pulls the job images.
|
|
|
|
## Entrypoint environment variables
|
|
|
|
The entrypoint ([`scripts/run.sh`](https://gitea.com/gitea/runner/src/branch/main/scripts/run.sh)) understands:
|
|
|
|
| Variable | Meaning |
|
|
| --- | --- |
|
|
| `GITEA_INSTANCE_URL` | instance to register against, e.g. `https://gitea.example.com/` |
|
|
| `GITEA_RUNNER_REGISTRATION_TOKEN` | registration token; unset before the daemon starts |
|
|
| `GITEA_RUNNER_REGISTRATION_TOKEN_FILE` | file to read the token from, for Docker/Kubernetes secrets |
|
|
| `GITEA_RUNNER_NAME` | runner name, defaults to the container hostname |
|
|
| `GITEA_RUNNER_LABELS` | labels, passed to both `register` and `daemon` |
|
|
| `GITEA_RUNNER_EPHEMERAL` | any non-empty value registers the runner as [ephemeral](../registration.md#ephemeral-runners) |
|
|
| `GITEA_RUNNER_ONCE` | any non-empty value runs a single job, then exits |
|
|
| `GITEA_MAX_REG_ATTEMPTS` | registration attempts before giving up, default `10` |
|
|
| `RUNNER_STATE_FILE` | registration file name inside `/data`, default `.runner` |
|
|
| `CONFIG_FILE` | config file inside the container, passed as `--config` |
|
|
|
|
These are entrypoint variables, not runner settings: the runner process itself is configured only through the [config file](../configuration.md).
|
|
|
|
Mount the config file when you need one:
|
|
|
|
```bash
|
|
docker run -v $PWD/config.yaml:/config.yaml -e CONFIG_FILE=/config.yaml ...
|
|
```
|
|
|
|
A config file can be generated with the image itself:
|
|
|
|
```bash
|
|
docker run --rm --entrypoint="" docker.io/gitea/runner:nightly gitea-runner generate-config > config.yaml
|
|
```
|
|
|
|
## docker compose
|
|
|
|
```yaml
|
|
services:
|
|
runner:
|
|
image: docker.io/gitea/runner:nightly
|
|
restart: always
|
|
environment:
|
|
CONFIG_FILE: /config.yaml
|
|
GITEA_INSTANCE_URL: "${INSTANCE_URL}"
|
|
GITEA_RUNNER_REGISTRATION_TOKEN: "${REGISTRATION_TOKEN}"
|
|
GITEA_RUNNER_NAME: "${RUNNER_NAME}"
|
|
GITEA_RUNNER_LABELS: "${RUNNER_LABELS}"
|
|
volumes:
|
|
- ./config.yaml:/config.yaml
|
|
- ./data:/data
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
```
|
|
|
|
When Gitea runs in the same compose project, depend on its health check so the runner does not try to register before the instance answers:
|
|
|
|
```yaml
|
|
depends_on:
|
|
gitea:
|
|
condition: service_healthy
|
|
restart: true
|
|
```
|
|
|
|
The rootless Docker-in-Docker variant needs a few extra options:
|
|
|
|
```yaml
|
|
services:
|
|
runner:
|
|
image: docker.io/gitea/runner:nightly-dind-rootless
|
|
restart: always
|
|
privileged: true
|
|
security_opt:
|
|
# for hosts running AppArmor (Ubuntu, Debian), whose default profile blocks
|
|
# the user namespace changes the bundled daemon needs
|
|
- apparmor=rootlesskit
|
|
volumes:
|
|
- ./data/runner:/data
|
|
environment:
|
|
- GITEA_INSTANCE_URL=<instance_url>
|
|
- GITEA_RUNNER_REGISTRATION_TOKEN=<registration_token>
|
|
- DOCKER_HOST=unix:///var/run/user/1000/docker.sock
|
|
# slirp4netns gives significantly better network throughput than vpnkit
|
|
- DOCKERD_ROOTLESS_ROOTLESSKIT_NET=slirp4netns
|
|
- DOCKERD_ROOTLESS_ROOTLESSKIT_MTU=65520
|
|
```
|
|
|
|
## Cache from a dockerized runner
|
|
|
|
A runner in a container creates a separate network per job by default, so the address it detects for its own cache server is often unreachable from job containers and `actions/cache` fails with a connection timeout. Set `cache.host` and `cache.port` explicitly and publish that port, or put the job containers on a shared network — see [Caching](../cache.md#dockerized-runners).
|
|
|
|
More deployment examples live in the [`examples`](https://gitea.com/gitea/runner/src/branch/main/examples) directory of the runner repository.
|