# Rootless Docker Gitea Runner Notes Reusable setup for running Gitea Actions jobs on a **rootless** Docker host when jobs need Docker access (Playwright, Buildx). The runner runs as a container; job containers reach the host's rootless socket. Substitute the rootless socket path `/run/user//docker.sock` for your runner user (examples use `3100`). ## Runner compose Mount the rootless socket into the runner container: ```yaml services: gitea-runner-1: image: gitea/act_runner:latest container_name: s4-runner-1 restart: unless-stopped environment: GITEA_INSTANCE_URL: https://gitea.example.com GITEA_RUNNER_REGISTRATION_TOKEN: ${GITEA_RUNNER_REGISTRATION_TOKEN} GITEA_RUNNER_NAME: s4-runner-1 CONFIG_FILE: /data/config.yaml volumes: - runner1_data:/data - ./config.yaml:/data/config.yaml:ro - /run/user/3100/docker.sock:/var/run/docker.sock volumes: runner1_data: ``` ## Runner config ```yaml container: docker_host: "-" # critical options: "-v /run/user/3100/docker.sock:/var/run/docker.sock" valid_volumes: - /run/user/3100/docker.sock # source path only ``` `docker_host: "-"` stops `act_runner` from adding its own socket mount on top of the one in `options`, which otherwise fails job creation with `Duplicate mount point: /var/run/docker.sock`. Keep `valid_volumes` as the host source path only (not the `src:dst` bind form). ## Workflow patterns - **Buildx**: use `docker/setup-buildx-action@v3` with `driver: docker`. The default driver starts a separate BuildKit container that misbehaves on this runner shape; the `docker` driver uses the mounted daemon directly. - **No setup-node cache**: drop `cache: pnpm` from `actions/setup-node@v4`; the job network often can't reach the Gitea Actions cache service (minutes of restore/save timeouts). Use `pnpm install --frozen-lockfile` instead. Revisit only once the cache-service network path is confirmed. - **Playwright**: split browser E2E from validation — build + upload artifact in a normal job, then run Playwright in the official image (`mcr.microsoft.com/playwright:v1.58.2-noble`) against the downloaded artifact. ## Host checks ```sh docker context show; echo "$DOCKER_HOST"; ls -l /run/user/3100/docker.sock; docker info # DOCKER_HOST should be unix:///run/user/3100/docker.sock docker run --rm -v /run/user/3100/docker.sock:/var/run/docker.sock docker:27-cli docker version ``` If that container can't use the socket, fix rootless Docker before touching the runner. ## Troubleshooting - **`Duplicate mount point: /var/run/docker.sock`** — set `docker_host: "-"`, keep the bind only in `options`, `valid_volumes` = source path only, recreate. - **`WARNING: IPv4 forwarding is disabled`** may persist even after host `net.ipv4.ip_forward=1`. `systemctl --user restart docker` and re-check `docker info`. If jobs create containers and reach their networks, treat it as stale output, not a failure. - **Buildx/build fails** — use `driver: docker`; confirm `docker version` works through the mounted socket. ## Useful commands ```sh docker ps --format '{{.ID}} {{.Image}} {{.Status}} {{.Names}}' docker logs --tail 120 s4-runner-1 docker compose up -d --force-recreate gitea-runner-1 tea actions runs --repo owner/repo tea actions runs logs --job --repo owner/repo ``` ## Known-good (Grindex / current runner) Host `s4.fisher.hu`; container `s4-runner-1`; image `gitea/act_runner:latest`; socket `/run/user/3100/docker.sock`; workflows in `.gitea/workflows`; Buildx `driver: docker`; setup-node cache disabled.