Starting with Gitea 1.24 and GARM v0.2.0, GARM supports Gitea as a forge alongside GitHub and GHES. You can manage runners for both GitHub and Gitea from the same GARM instance.
- Using GARM with Gitea
This quickstart deploys both Gitea and GARM using Docker Compose with LXD as the runner provider.
- Ubuntu host with Docker and LXD installed (
sudo lxd init --auto)
Important
Docker and LXD can conflict on iptables. If LXD containers lose internet, run:
sudo iptables -I DOCKER-USER -j ACCEPTsudo mkdir -p /etc/gitea /etc/garm
sudo chown 1000:1000 /etc/gitea /etc/garmnetworks:
default:
external: false
services:
gitea:
image: docker.gitea.com/gitea:1.25.5
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
volumes:
- /etc/gitea/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "80:80"
- "22:22"
garm:
image: ghcr.io/cloudbase/garm:nightly
container_name: garm
restart: always
volumes:
- /etc/garm:/etc/garm
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/snap/lxd/common/lxd/unix.socket:/var/snap/lxd/common/lxd/unix.socket
ports:
- "9997:9997"Follow the same config files as the Docker Quickstart, but set port = 9997 in [apiserver].
docker compose up -ddocker exec -u=1000 gitea \
gitea admin user create \
--username testing \
--password superSecretPassword \
--email admin@example.com \
--admin \
--must-change-password=falseLog into Gitea and create an organization (e.g., testorg) and a repository (e.g., testrepo).
sudo docker cp garm:/bin/garm-cli /usr/local/bin/garm-cli
sudo chmod +x /usr/local/bin/garm-cli
garm-cli init --name="my_garm" --url http://<YOUR_IP>:9997garm-cli gitea endpoint create \
--api-base-url http://<YOUR_IP>/ \
--base-url http://<YOUR_IP>/ \
--description "My Gitea server" \
--name local-giteaCreate a Gitea PAT with write:repository and write:organization scopes:
LOGIN=$(curl -s -X POST http://localhost/api/v1/users/testing/tokens \
-u 'testing:superSecretPassword' \
-H "Content-Type: application/json" \
-d '{"name": "garm-token", "scopes": ["write:repository", "write:organization"]}')
TOKEN=$(echo $LOGIN | jq -r '.sha1')
garm-cli gitea credentials add \
--endpoint local-gitea \
--auth-type pat \
--pat-oauth-token $TOKEN \
--name gitea-token \
--description "Gitea runner management token"garm-cli repo add \
--credentials gitea-token \
--name testrepo \
--owner testorg \
--random-webhook-secret \
--install-webhook
garm-cli pool add \
--repo <REPO_ID> \
--provider-name lxd_local \
--image ubuntu:24.04 \
--tags ubuntu-latest \
--flavor default \
--os-arch amd64 \
--os-type linux \
--enabled=true \
--min-idle-runners=1 \
--max-runners=5Check the runner:
garm-cli runner list
garm-cli runner show <RUNNER_NAME>If GARM is on a private or internal IP address, Gitea will block webhook deliveries by default. You need to add GARM's address to Gitea's allowed host list in app.ini:
[webhook]
ALLOWED_HOST_LIST = privateSetting ALLOWED_HOST_LIST = private allows webhooks to private network addresses. See the Gitea documentation for more options.
After changing app.ini, restart Gitea for the setting to take effect.
- Runner binary: Gitea uses
act_runnerinstead of the GitHub Actions runner. GARM handles the differences transparently. - Enterprise level: Not available for Gitea
- Scale sets: Not available for Gitea (GitHub-only feature)
- GitHub Apps: Gitea uses PATs only