Repository Sync Guide

GitLab repo to architecture diagram: subgroups, read_api, and a picture that keeps up

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

GitLab teams tend to have the architecture written down in three places at once: the .gitlab-ci.yml, the terraform directory, and a wiki page that stopped being true two releases ago. A repository connection reads the first two on a schedule and produces the third, so the wiki page stops being the thing that has to be remembered.

7 min readFor teams on gitlab.com, including deeply nested subgroup projects

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

152/20003 credits left
Try:

No account needed · Editable canvas, not a picture

Connecting a gitlab.com project

GitLab addresses a project by its full path, and so does the connection. Paste group/project, group/subgroup/project, or the browser URL, and the path is normalized to the canonical form before it is stored. Nesting is not a special case here: the API is called with the URL-encoded full path, so a project four levels down a subgroup tree connects exactly like a top-level one.

A private project takes a personal access token with the read_api scope. That is the scope that covers what the sync actually calls: listing branches and tags, listing the repository tree, and reading raw file contents. A public project needs no token. The token is validated by listing refs before it is stored encrypted, and only its last four characters come back afterwards.

Each diagram then tracks one branch or tag of that project. A connection can back several diagrams, which is the usual answer for a group that ships from main and cuts release tags: one living diagram on main, one frozen picture per tag.

What differs on GitLab under the hood

The tree listing is called as recursive=true&per_page=100&pagination=keyset, and the next page comes from the RFC 5988 Link header rel="next" rather than from an incremented page number. That is the pagination mode GitLab documents for large recursive listings, and it is what keeps a big project from answering with a partial tree that looks complete. The walk stops at 60 pages or 20,000 entries and reports itself as truncated rather than pretending it saw everything.

One quirk shapes the reading step. A GitLab tree entry is id, name, type, path and mode; there is no size field, while GitHub and Azure DevOps both report one, which lets selection drop an oversized file before fetching it. On GitLab there is nothing to drop on, so the caps move to fetch time: content comes from the raw file endpoint, anything measuring over 400KB once read is discarded from the corpus, and anything past 30KB is cut and marked in place with the line "[... truncated at 30KB by Datadef repo sync ...]" so a reader can tell a short file from a shortened one.

Hosting is gitlab.com only for now. A self-managed GitLab instance, including the ones sitting behind a VPN, is not connectable yet, and no user-supplied base URL is accepted anywhere in the connection flow.

Accepted GitLab repository references

  acme/platform
  acme/infra/platform                    (subgroup)
  acme/infra/edge/platform               (nested subgroup)
  https://gitlab.com/acme/infra/platform (browser URL)
  https://gitlab.com/acme/platform.git   (clone URL)

The .gitlab-ci.yml problem, and what the sync does with it

CI files are architecture in disguise: the stages name the deployable units, the jobs name the environments, and the include statements pull in half of it from somewhere else. Selection matches .gitlab-ci.yml by exact filename and puts it in the CI class, which takes up to 8 files by default and 10 when the diagram is set to the architecture focus, so the deployment story in the generated doc comes from the pipeline definition rather than from a guess about it.

The honest limit is the include graph. Only the root .gitlab-ci.yml matches the CI rule, so a pipeline split across .gitlab/ci/build.yml and .gitlab/ci/deploy.yml contributes those files as directory counts in the tree summary and nothing more, and a remote include from another project is never fetched at all. The generated architecture.md is grounded in the files that were actually read, and its rules are explicit: a section the files do not answer says so in one line instead of inventing a plausible answer. Keeping the stage and environment names in the root file is what makes the deployment section specific.

For pipeline-shaped diagrams specifically, living diagram from GitLab CI covers the include resolution problem on its own terms. The repository connection is the wider view: the pipeline is one of seven file classes it reads.

Keeping it visible inside GitLab

The generated diagram embeds as a live image in a project README, a GitLab wiki page, or a merge request description, using one markdown line. It updates when the canvas does, so nobody has to remember to re-export anything. The details of where GitLab renders external images are in embed diagrams in GitLab.

Alongside it, the architecture.md carries the component inventory, the data flow, the deployment path, and directory notes, in plain markdown you can copy into the repo or into the wiki. Both come from the same sync, so they cannot disagree with each other about what exists.

One connection, several diagrams

A GitLab connection is the project plus its token. Diagrams hang off it one per ref, which is how a group keeps a living picture of main and a frozen one per release without connecting anything twice. See branch or tag.

FAQ

Which GitLab token scope does the sync need?

read_api on a personal access token. That scope covers listing branches and tags, listing the repository tree, and reading raw file contents, which is everything the sync calls. Public projects need no token, and nothing is ever written back to the project.

Do subgroup projects work?

Yes, at any depth. GitLab addresses a project by its URL-encoded full path, so group/subgroup/project and deeper nestings connect the same way a top-level project does. Pasting the browser URL works too, since it is normalized to the canonical path.

Is self-managed GitLab supported?

Not yet. The supported hosts are gitlab.com, github.com, and dev.azure.com, and the connection flow accepts no custom base URL, so a self-managed hostname is rejected before a request is made. A team on a self-managed instance can still reach the same canvas through the MCP server, where a coding agent reads the checkout locally and draws from it.

Why does GitLab need different size handling than GitHub?

Because the GitLab tree API returns paths and types without file sizes. GitHub and Azure DevOps report sizes, so an oversized file can be skipped before it is fetched. On GitLab the caps are applied at read time instead, with anything past 30KB truncated behind a visible marker.

Can the diagram appear in a GitLab wiki page or a merge request?

Yes. The linked project exposes a live embed image URL, and one markdown image line renders it in a README, a wiki page, or a merge request description. The image follows the canvas, so it reflects the most recent sync without anyone re-exporting a file.