Creating a trigger
A trigger is the object that decides when a pipeline runs. Connecting a repository and committing a .loha.yml is not enough on its own: without a trigger, nothing ever fires.
This guide covers creating one, the webhook behind it, and rotating its secret.
Prefer to ask an agent?
An AI client connected to Loha's MCP server can show you what already exists, which is the fastest way to tell whether a repo is wired up:
- "List the triggers in my Loha workspace and tell me which repositories have none."
Creating a trigger is dashboard-only for now. Agents can read triggers and run them, but not create them. Using AI with Loha
Create one
From Triggers → New trigger, or from a repository's Triggers tab.
The first choice is the firing mode, and it changes every field after it:
| Mode | Fires when | Use it for |
|---|---|---|
| Repository | Your Git host sends a webhook for a repo you've connected | Normal CI: build on push, on pull request, on tag |
| Webhook | Any external system calls a URL Loha gives you | Nightly jobs from a scheduler, deploys from another tool |
| Manual | You click Run now | On-demand jobs, one-off deploys |
Presets cover the common shapes (push to main, merge requests, release on tag, external system, on demand) and just prefill the fields below, so you can start from one and adjust.
Repository mode
Pick the event repository. The webhook dialect Loha expects follows from that repo's host, so there's nothing to choose: GitHub, GitLab, and Gitea are each detected, and Forgejo (including Loha Repos) uses Gitea's dialect because it sends Gitea's headers.
Then pick the events:
| Event | Fires on |
|---|---|
push | A commit pushed to a branch |
pull_request | A pull/merge request opened, updated, reopened, or closed |
tag | A tag pushed |
tag is its own event, not a variant of push.
Branch filters narrow it further. An empty filter means every branch.
Only the trigger decides whether a build starts. Inside a build, a job or step if: can skip work by branch or event, but it cannot stop the build from being created. For per-branch or per-tag pipelines you can also create separate triggers: one filtered to main running your staging config, another filtered to tags running your production config.
Webhook mode
No repository, and you choose the provider yourself. Loha gives you a URL and a secret; whatever calls that URL starts a build.
Pick generic unless the caller is a forge sending that forge's signature format.
Schedule mode
Pick On a schedule, then either click a preset (every hour, every night at 02:00, weekdays at 09:00, every Monday) or type a cron expression. The form shows the next three runs in Thai time. Rules:
- Five fields (
minute hour day-of-month month day-of-week). Three-letter names such asMON,JULormon-friare allowed.@daily-style aliases and theL,W,#extensions are not accepted. - As in classic cron, if you restrict both day-of-month and day-of-week, the schedule fires when either matches.
- Never more often than every 15 minutes; your limit preset may require a longer gap.
- The number of enabled scheduled triggers per workspace is capped by your preset. Disable one to make room.
- A repository is optional. With one, the config source can be the committed
.loha.ymlon the chosen branch; pushes to that repository never fire the trigger.
Skip if there are no new commits. With a repository chosen, you can turn this on to save builds on quiet days. Before each scheduled run, Loha looks at the head of the branch the schedule builds (the trigger's branch, main if none is set) and compares it with the commit of the last scheduled run. If nothing new was committed, that slot is skipped and appears in Trigger activity with the reason No new commits since last scheduled run. A failed scheduled run does not count, so the next slot runs again even with no new commits. If Loha cannot reach your Git host to check, the run happens anyway. The option needs a repository: without one there is no branch to compare, and the switch stays off.
A scheduled trigger can still be run by hand unless you turn off Allow manual run. The trigger page shows Next run and Last scheduled run; every firing, including a skipped one, appears in Trigger activity with its reason. If the schedule can no longer be computed (for example an unparseable cron), the trigger stops firing and its page shows a warning: edit the cron to fix it.
Where the pipeline comes from
Every trigger has a config source:
- Repo file: read
.loha.ymlfrom the repository at the commit that triggered the run. This is the normal choice: your pipeline is versioned with your code. - Inline: the YAML lives on the trigger itself. Useful when the pipeline shouldn't be in the repo, or when the repo isn't yours.
You can also set a resource class on the trigger, which overrides what the config asks for.
The webhook in your Git host
Loha registers it for you when it can. Creating a repository-mode trigger on GitLab, GitHub, Gitea or Forgejo makes Loha add the webhook to that repository itself, using the access token stored on the repo host. When that works the dialog says so, there is nothing to copy, and the next matching push starts a build.
Whether it can comes down to one thing: the stored token has to be allowed to manage webhooks, which is a wider permission than the read access Loha otherwise needs. See what permissions the token needs. If it isn't, nothing breaks. Loha hands you the URL and secret and you add them by hand, exactly as below.
Triggers that predate this
A trigger created before Loha registered webhooks, or one whose registration was refused at the time, shows a Register webhook button on its Settings tab. Pressing it does the same thing creating a trigger does, and it is safe to press even if you already added the webhook by hand: Loha looks for a hook already pointing at this trigger and updates that one rather than adding a second. It is also how you retry after widening the token.
Two cases are always manual, by nature:
- External webhook triggers (
generic), where the caller is your own system and there is no API for Loha to register anything against. - Any repository on a host Loha has no client for.
Adding it by hand
Copy the webhook URL and secret from the dialog shown on creation, then add them in your Git host:
| Host | Where | Secret field |
|---|---|---|
| GitHub | Settings → Webhooks → Add webhook | Secret: signed as X-Hub-Signature-256 |
| GitLab | Settings → Webhooks | Secret token: sent verbatim as X-Gitlab-Token |
| Gitea / Forgejo | Settings → Webhooks → Gitea | Secret: signed as X-Gitea-Signature |
| Anything else | — | Generic HMAC in X-Loha-Signature |
GitLab is the odd one out: it sends the secret as a token, not as an HMAC of the body. Everything else signs the payload.
Select the events in your host that match the events you chose on the trigger. If they disagree, Loha receives deliveries it ignores.
It's revealed on creation and never again. If you lose it, rotate: you can't read it back. When Loha registered the webhook for you the secret is never shown at all: it went straight onto the hook, so there is nothing for you to keep.
Test it
Push a commit that matches your filter, or use Run now on the trigger. A new build should appear under Builds within a second or two.
If nothing happens, check your Git host's webhook delivery log first: it shows the response Loha returned. A 401 means the secret doesn't match; a 404 means the URL is wrong.
Rotating the secret
Rotate secret on the trigger's Settings tab issues a new secret.
If Loha registered the webhook, it also pushes the new secret to your Git host, and there is nothing to copy. If that push fails (an unreachable host, a token that has since lost its permission), Loha says so and names the date the trigger will stop firing, and shows you the secret so you can update the host by hand before then. Don't dismiss that one: the rotation has already happened in Loha, so the hook out there is on a clock.
For a webhook you added by hand, the old secret keeps working for 7 days. That overlap exists so you can update your Git host without dropping deliveries: rotate, paste the new secret into the host, and the old one expires on its own. You don't need to do both at the same instant.
Deleting a trigger
Deleting stops future runs. Builds it already produced stay in your history. If Loha registered the webhook, it is removed from your Git host too, so there's no dead hook left POSTing at a URL that no longer exists.
Following the pipeline file instead
By default a trigger carries its own event and branch filters, and the on: block in your
.loha.yml is not consulted. If you would rather your rules live in one place — in git, travelling
with the code — turn on Follow the pipeline file on a trigger whose pipeline comes from a
committed file.
In that mode:
- the
on:block of the.loha.ymlat the commit that was pushed decides whether a build runs; - the event and branch fields on the trigger are replaced by a note pointing at the file;
- moving a branch rule is a commit, reviewed like any other change.
The trade-off is that every push reaches Loha and is evaluated, so a branch-heavy repository produces more skipped entries on the Activity tab. Filter it by outcome to see only what built.
Switching an existing trigger
Changing the mode can change which pushes build, so Loha shows you what would change before you save it. Tick or untick Follow the pipeline file on an existing trigger and it replays that trigger's recent events under the new mode:
3 of the last 20 events would have decided differently. A push to
feat/cachethat built would now be skipped — Branch does not matchon.push.branches
If nothing would change, it says so. If it cannot check — the config is unreadable, or the trigger has no recent events — it says that too, rather than implying the switch is unsafe. It never blocks the save; it is there so the change is not a blind toggle.
One limitation, which the preview states itself: it reads your pipeline file once, at the default
branch. Follow mode reads it at each pushed commit, so if the on: block changed recently the real
result can differ from the forecast.
See also
- Triggers: what a trigger is, and how runs are attributed
- Your first pipeline: the end-to-end walkthrough
- Repositories & Git hosts: connecting the repo in the first place
- Service accounts: running a trigger as a machine identity