Mirror GitLab Projects to Gitea

GitLab is a source now

Gitea Mirror started as a GitHub to Gitea tool, and that is still the default and the best tested path. Since version 3.30 the connection card has a Source dropdown, and GitLab is one of the options. It is marked beta: it is tested end to end against gitlab.com and a self-hosted instance before every release, but it has had less time in the field than GitHub.

The mechanics are the same as for GitHub. Gitea does the actual mirroring through its pull mirror feature, which treats GitLab as a plain git remote. Gitea Mirror’s job is to find your projects through the GitLab API, create the mirrors in the right place, keep them on a schedule, and notice when a project disappears upstream.

Requirements

Step-by-step

1. Pick GitLab as the source

Open Configuration → Connections. At the top of the source card, change the Source dropdown from GitHub to GitLab. An Instance URL field appears. Leave it empty for gitlab.com, or enter the base URL of your own instance, for example https://gitlab.example.com. The username field takes your GitLab username.

If you already imported repositories from GitHub, the dropdown shows a lock note and a Change button instead. Changing the source is allowed, but only after a confirmation dialog, and the repositories you imported before stay tied to GitHub. More on that below.

2. Create the token

In GitLab go to Preferences → Access tokens and add a token with read_api and read_repository. Paste it into the token field and click Test. The test calls the selected instance with that token and tells you whether it can see your account.

You can skip the token for public projects. Without one, Gitea Mirror only sees the public projects of the configured user, plus their starred projects, and adding a single public project by URL still works.

3. Import projects

Click Import GitLab Data in the top right of the Configuration page, or wait for the scheduler if you turned on auto import. Discovery lists the projects the token can see, including private ones and projects in groups you belong to.

To add one project without importing everything, open Repositories → Add Repository and paste the project URL. Nested paths are fine: https://gitlab.com/group/subgroup/project fills the owner and name fields from the URL, and a deep link into a merge request or a file tree is trimmed automatically.

4. Mirror and schedule

Mirror the projects from the Repositories page as you would for GitHub. On the Automation tab, enable Automatic Syncing and pick an interval. Gitea’s own pull mirror interval is not used; Gitea Mirror triggers the sync itself so the schedule you set is the schedule you get. Turn on Auto-mirror new repositories if new projects should be picked up without a manual import.

How GitLab groups land in Gitea

GitLab nests groups, Gitea does not. A project at group/subgroup/project is mirrored into the Gitea organization group when the strategy is Preserve Structure, and the repository keeps group/subgroup/project as its full name in the Gitea Mirror table so you can still tell subgroups apart. The organization filter in the mirror settings matches the top level group.

Two smaller details:

What comes across, and what does not yet

The git side is complete. Code, every branch and tag, the wiki when enabled, and LFS objects are mirrored for GitLab exactly as for GitHub, and scheduled sync, auto import, cleanup of projects deleted upstream, starred projects and private projects all work.

The metadata side is GitHub only for now. Issues, merge requests, labels, milestones and releases are not read from the GitLab API yet, so those switches are disabled on the Configuration page and the mirror step skips them. Force push detection also depends on the GitHub API and is unavailable for GitLab sources. The issue and pull request playbook only applies to GitHub sources.

Several sources at once

GitHub and GitLab sources can be connected at the same time, and each repository remembers the source it came from. Cleanup checks each source’s own repositories, and mirroring a repository uses that source’s credentials. Repositories whose source was later removed keep syncing on the Gitea side, because Gitea holds their clone configuration, but re-mirroring, metadata and cleanup skip them.

Each source locks once repositories are imported from it. Its provider and instance URL stay fixed until you confirm a change.

Environment variables

For a deployment that should come up configured, set:

SOURCE_PROVIDER=gitlab
SOURCE_URL=https://gitlab.example.com   # omit for gitlab.com
GITHUB_USERNAME=your-gitlab-username
GITHUB_TOKEN=glpat-...

The GITHUB_* names are kept for every source so an existing deployment only has to add SOURCE_PROVIDER. The full list is on the environment variables page, and the sources page has the capability table.

FAQ

Does this work with a self-hosted GitLab behind a private CA?

Yes, if both Gitea Mirror and Gitea trust the CA. Gitea Mirror talks to the GitLab API, Gitea does the clone. See custom CA certificates.

Can I mirror GitHub and GitLab into the same Gitea?

Not from one account. Each Gitea Mirror user has one source, so create a second user for the second source. Both can point at the same Gitea server.

Will merge requests ever be mirrored?

Reading merge requests from the GitLab API is the natural next step after the code path settled. Watch the GitHub issues for progress.