Repositories & Git hosts
Loha is a cloud platform that connects to your existing Git host; it doesn't store your source. You connect a repository once; from then on Loha can receive its push and pull-request events, read its .loha.yml, and run builds for it.
Supported Git hosts
| Host | Status | Notes |
|---|---|---|
| GitLab | ✅ Supported today | Self-managed GitLab or GitLab.com, connected with an access token. |
| GitHub | ✅ Supported today | github.com or GitHub Enterprise Server, connected with an access token. |
| Gitea | ✅ Supported today | Self-managed Gitea instances, connected with an access token. |
| Forgejo | ✅ Supported today | Self-managed Forgejo instances, connected with an access token. |
All four hosts support the same core flow and connect the same way: a name, an API URL, and an access token. Once connected, Loha can list your repositories, read .loha.yml, run builds, and report commit/PR status back. If your host isn't listed yet, the roadmap tracks when to expect it.
A GitHub App installation is a separate mechanism that is not yet available to set up from the portal; connect GitHub with a token. See Repo hosts for the detail, and for the token scopes each provider needs.
You connect a host once per workspace: it is saved as a repo host and reused by every repository you connect afterwards.
What a repository is in Loha
A repository in Loha is a connection to one repo on your Git host, scoped to a workspace. It carries:
- the link to the source repo (and the access token used to read it);
- the triggers that decide when a build runs;
- the builds that have run for it.
Loha never copies your source into long-term storage: each build clones the repo into ephemeral, in-cluster workspace storage and discards it when the build ends. See Sovereignty for the data-residency guarantees.
Connecting a repository
- Add a repo host for your Git provider, under Settings → Repo hosts: a name, the API URL, and an access token that can read your repositories. Loha stores the token encrypted. Do this once; every repository afterwards reuses it. (Which scopes the token needs differs per provider: a token that can read files but not list repositories connects cleanly and then finds nothing.)
- In the dashboard, open your workspace and go to Repositories → Connect repository. You can also add a repo host inline here if you haven't already.
- Pick the repo host, then pick the repository to connect from the list Loha discovers. There is no per-repository token to paste.
- Usually already done for you. If the repository has a committed
.loha.yml, connecting it creates a webhook trigger and registers the hook. If it has none, the repository page offers to add a starter pipeline. Create one by hand only when you want a second trigger, an inline pipeline, or a manual-only entry point. Its detail page shows a webhook URL and a secret token; add both to your Git host's webhook settings (GitLab: Settings → Webhooks; GitHub: Settings → Webhooks; Gitea and Forgejo: Settings → Webhooks).
Loha adds the webhook for you when the access token on the repo host is allowed to manage webhooks; see the scopes table. If it isn't, Loha hands you the URL and secret and you add them yourself, a one-time step per trigger. Deleting the trigger removes a webhook Loha created; one you added by hand is yours to remove. Once the webhook is in place, pushes and pull requests reach Loha and can fire triggers.
If you're moving from GitLab CI or GitHub Actions, the migration guides walk through connecting the host and translating your existing config: from GitLab CI, from GitHub Actions.
After you connect: what state is the repository in?
The repository page says, in one sentence, whether it is building — and if not, why.
| It says | What it means | What to do |
|---|---|---|
Building on every push to main | A trigger exists and its webhook is on your Git host. Nothing to do. | — |
| Not building — webhook not wired | The trigger exists, but no webhook reached your host, so pushes never arrive. | Re-register it from the trigger page |
| Not building — no trigger | Nothing is listening. | Set up building (below) |
Adding a starter pipeline
If the repository has no .loha.yml, the repository page offers Set up building. Loha reads a
few well-known files — a Dockerfile (including Dockerfile.prod and docker/Dockerfile), your
lockfile, package.json scripts, .nvmrc, go.mod, requirements.txt — and builds a pipeline
that matches them: your package manager, only the scripts you actually have, your Node, Go or Python
version, and your default branch. Why this pipeline lists every file it relied on.
A repository with a Dockerfile gets a container build that pushes to the registry in
Settings → Registries, as <registry>/<repository>:<commit> on the oldest registry listed there — edit IMAGE if your registry needs a project or group path. With no registry yet, the image is built but not pushed until you add one.
A Node.js service with a start or start:prod script (npm or yarn classic) but no Dockerfile also gets a generated multi-stage Dockerfile (non-root, digest-pinned node:22-alpine base unless your repository asks for another Node version) when the workspace has a registry. It installs with npm ci (or yarn install --frozen-lockfile) when a lockfile is committed, otherwise with npm install (or yarn install) so the first build does not fail — commit a lockfile for reproducible builds. It is committed together with .loha.yml, by commit or merge request, so it is yours: edit it, and security fix pull requests can update its base image. Because it must live in your repository, Save in Loha instead is not offered in that case. The generated image uses Alpine Linux; packages that compile native modules may need build tools, so edit the Dockerfile to add them.
The tests run before the image build, in the same pipeline, so a failing test stops the image from being built. Images are built only on pushes to the default branch; a merge request runs the tests only. Go test steps get a 30-minute timeout (other languages get 10 minutes), because a first Go build downloads and compiles every module.
You then choose one of three things, and all are offered from the start:
| Choice | What happens |
|---|---|
| Open a merge request | The .loha.yml is added on a new branch and a merge request is opened into your default branch. Loha sets up building at the same time, so merging the request starts the first build. Pushes to the default branch before the merge have no .loha.yml yet and show as Skipped. If building could not be set up then, the dialog offers I merged it — set up building after you merge. Opening it again reuses the same branch and merge request. |
Commit to <branch> | The .loha.yml is committed to your default branch. The file is yours, in git. |
| Save in Loha instead | The same pipeline is stored on the trigger. Your repository is not modified. |
The commit is a push to your default branch, and the trigger Loha creates is listening for pushes — so your first build starts immediately. That is intentional.
Neither option is a fallback for the other. Loha cannot tell in advance whether your access token is allowed to write to the repository, and a read-only token or a protected default branch is a perfectly normal setup. If the commit is refused, the dialog stays open, tells you why, and the Save in Loha instead option is still there.
A pipeline saved in Loha can be moved into git later: commit the file yourself, then point the trigger at it.
What happens after connecting
- A push or pull request (matching a trigger) starts a build from the
.loha.ymlcommitted in the repo. - Every event Loha receives — whether or not it built — is listed on the repository's Activity tab, with the reason for each outcome. That is where to look when a push did not produce a build.
- You can also run a build on demand from the dashboard's Run button, or, soon, from the Loha CLI (
loha deploy).
Where to go next
- Repo hosts: connecting, validating, and rotating your Git provider connection
- Triggers: what causes a pipeline to run
- Pipelines: what runs when a trigger fires
- Your first pipeline: connect a repo and ship a build end-to-end