# Lightweight shared Gitea runner The existing global registration and `deploy` label are retained. The runner is not privileged, has no Docker socket, drops all capabilities, and now runs as UID 10001 with a read-only root filesystem and read-only registration mount. Job workspaces live in bounded tmpfs and do not persist across container restarts. Job capacity is one; timeout is 60 minutes. The deployment SSH credential remains repository-scoped and its CORP forced command accepts only the current master SHA. The derived `0.2.13-hardened` image adds a real `runner` passwd/group entry for UID/GID 10001. Setting a numeric Compose user without this entry breaks OpenSSH with `No user exists for uid 10001`. The installer builds from the already installed base image without network access, then verifies UID resolution and `ssh -G` in a read-only, network-disabled container before replacing the running service. Install this directory's files on the Gitea server and run `sudo python3 /path/to/install-runner-hardening.py` only after pausing assignment of new jobs in Gitea and waiting for the current job to finish. Resume assignment after the installer confirms the runner is healthy. An idle `ps` snapshot alone is insufficient: the runner could receive a new job before container replacement. The installer does not pause scheduling automatically or verify that pause. The installer backs up configuration and restores it if the runner cannot rejoin. **Trust boundary:** `deploy:host` executes all jobs as the same container user. It is not a security boundary between repository authors. Keep workflow-writing permissions restricted to trusted developers. Strict isolation requires separate ephemeral job environments; the shared host runner must not run untrusted forks. Container restrictions do not make it safe for mutually untrusted tenants.