TurboPanel Docs
Using TurboPanel

Git sources and repositories

A service built from source needs three things the platform keeps apart on purpose: an application that lets TurboPanel talk to a provider, a repository the organization has told the platform about, and the binding in the compose document that says which service builds from it. This chapter covers Projects → Git sources (applications and their installations), Projects → Repositories (the ledger of repositories), the x-turbopanel.source binding, and push-to-deploy.

The model

LayerWhat it isWhere
ApplicationA GitHub App or a GitLab OAuth application registered with the provider. Organization-owned, or shared (registered by an administrator under Admin → Git providers for everyone; shown read-only to organizations). A control plane may hold several — github.com beside GitHub Enterprise, one App per customer account, gitlab.com beside a self-managed GitLab.Projects → Git sources
Installation / connectionWhat the provider grants the application: a GitHub App installation on an account (with the repositories it may see), or a GitLab OAuth connection for a user.The application's detail page, Repository access
RepositoryOne row per canonical repository URL in the organization, recording how it authenticates (its Access), its default branch, and whether pushes deploy. Created the first time a service picks it.Projects → Repositories
Bindingx-turbopanel.source on a service: the repository, the branch (which an environment can override), and the build.The service's Source section on the Services tab; an environment's Git branch panel

Three rules:

  1. An installation belongs to one organization. Connecting a GitHub installation binds it to the organization that completed the install; another organization cannot claim it (installation_claimed_by_another_organization). Since 2026-09-17 the callback also requires the authorizing GitHub user to be able to see the installation, so an installation id cannot be guessed.
  2. The provider is never trusted with a guess. A self-managed GitHub Enterprise or GitLab URL must be https, carry no credentials, name no reserved host, and resolve to a public address (forge_url_rejected) — it is fetched with the application's credentials, so an internal address would turn the control plane into a proxy.
  3. Webhooks need a public URL. Push-to-deploy works when the provider can reach the control plane at /webhook/github or /webhook/gitlab. On a LAN-only control plane the application's page says so and the alternative is a manual deploy.

Before you begin

  • Manage rights on the organization.
  • For GitHub: a GitHub account (personal or organization) you can install Apps on.
  • For GitLab: an OAuth application on the GitLab instance with the callback the app shows you.
  • For push-to-deploy: the control plane's public URL configured (Admin → Access → Hostnames on self-hosted).

Connect GitHub

Projects → Git sources → Create a GitHub App. Choose its Owner: your Personal account or a GitHub Organization (give its github.com/<login> slug — not a TurboPanel organization).

GitHub opens its manifest flow with the webhook URL and callback already set. Approve it; GitHub creates the App and returns you to the application's page (?created=), with every credential stored — secrets are write-only from here on.

On the application's page, Install repositories (a 302 to GitHub). Pick the account and the repositories the App may see. GitHub sends you back with the installation claimed for this organization.

Repository access now lists the installation and its repositories. Sync from GitHub refreshes the list after you change it on GitHub. An installation GitHub has suspended shows Suspended.

Register an existing one (next to Create a GitHub App) takes an existing App's id, private key and secrets instead of the manifest flow — for an App you already own, or on GitHub Enterprise (give its base URL).

Connect GitLab

Projects → Git sources → Register an existing one → GitLab: the GitLab base URL (gitlab.com or your own), and the OAuth application id and secret you registered there with the callback URL the form shows.

Add application, then Connect a GitLab account — a 302 to GitLab. Authorize; GitLab returns you with a connection for your user.

Bind a repository to a service

On the Services tab, open the service and its Source section. Pick the application, then the account (installation or connection), then the repository, then the branch.

Optionally set a subdirectory, a build command, an output directory (for a static site) and, for a Node app, a start command.

Save. The repository row is created under Projects → Repositories if it did not exist. The service builds a release from the branch's head on the next deploy.

The same picker serves the create wizard's Repository card, which is the fastest way from a repository to a running service.

Plain git (deploy keys)

A repository the providers do not host — or one you would rather not connect an application for — can be added by URL. An https URL with no credentials is read anonymously; an SSH URL needs a deploy key (source_ssh_requires_credential), which the platform generates and shows you once to add on the repository. Plain git repositories are read through a connected server rather than the provider's API, cannot be refreshed (source_refresh_not_supported), and never deploy on push.

Push-to-deploy

Two things decide whether a push deploys, and they are kept apart: the repository says whether pushes are allowed at all, and each environment says which branch it builds.

Each repository row carries Auto-deploy:

ModeWhat a push does
disabled (default)Nothing. Deploy from the app.
immediateDeploys every environment that builds the pushed branch, as soon as the push arrives.
checks_passedParks the pushed commit and deploys it when the provider reports the commit's checks passed (a GitHub check suite, a GitLab pipeline). A newer push replaces the parked commit.

One branch per environment

An environment builds the branch its services name (x-turbopanel.source.branch), or the repository's default branch when none is named. Because an environment's own compose is laid over the project's, one project can build different branches in different environments from the same repository: production builds main, staging builds staging. A push to staging deploys only staging; production is left alone.

To give an environment its own branch, open it and use its Git branch panel: for each Git-bound service it shows the branch that environment builds and where it came from (this environment, the project, or the repository's default), a Branch for this environment field, and Follow the project to remove the override. The panel saves a small override in the environment's own compose; it does not copy the service. A service that runs a plain image cannot be overridden this way — change it in the compose editor.

Deploy on push (in the same panel, and in the service's Source section) turns push deploys off for that service. The environment can still be deployed by hand; only the push is ignored. If the repository's own Auto-deploy is disabled, the app says so and offers to turn it on, because until it is on no push deploys anything.

Matching is exact: branch names are case-sensitive, refs/heads/staging and staging are the same branch, and tags and other references are never branches. A service with no branch of its own and a repository with no recorded default branch is never deployed by a push — name a branch.

What a push-triggered deploy looks like

The deploy goes through the same path as an app deploy, as the system, and is recorded in the environment's Deployment history as Push to <branch> with the short commit it built. Several services of one repository on different branches in one environment each build their own branch's commit.

A push that cannot deploy is answered and dropped, not retried: no environment builds that branch, the environment has no server, the branch was deleted, push deploys are off for it, or the deploy pipeline refused it for a reason a retry would not fix (the same refusals an app deploy gets — see Deploying). Only a control plane-side failure makes the provider redeliver.

With checks_passed, a repository parks one commit at a time, so when several environments build different branches a newer push to another branch replaces a parked commit that has not yet seen its checks pass.

The webhook URL on each application's page carries that application's routing reference (/webhook/github/<ref> on self-managed providers), so a delivery names its application before any secret is checked; a delivery whose signature does not match is refused.

Repositories — the ledger

Projects → Repositories lists every repository row: its provider, its Access (connection, deploy key or anonymous), the tracked branch, whether the provider's default branch has drifted from it, its auto-deploy mode, and the projects using it. Refresh re-reads the default branch and last commit through the provider. Delete works only for a repository no compose document references (source_referenced_by_compose).

Reference

ItemValue
Providersgithub · gitlab · git
Auto-deployimmediate · checks_passed · disabled (default)
Repository URL≤ 2048 characters, https (or ssh with a deploy key)
Branch≤ 255 characters
Webhook paths/webhook/github, /webhook/gitlab; self-managed providers append /<ref>
GitHub callback<origin>/api/client/v1/repositories/github/callback, with Request user authorization during installation on
Forge URL rulehttps only, no credentials, no reserved names (localhost, .local, .internal, .arpa, single labels), public address

Errors

CodeStatusMeaning
github_app_not_configured, gitlab_oauth_not_configured503No application of that kind is available to this organization or the control plane.
public_url_not_configured, gitlab_redirect_uri_unknown503The control plane has no public URL, so a manifest or OAuth redirect cannot be built.
webhook_origin_not_published400The application's webhook origin is not among the control plane's public URLs.
forge_url_rejected400The self-managed provider URL failed the safety rule above.
invalid_manifest_request400The GitHub App manifest request is malformed.
git_app_required400The repository needs an application to be read.
git_app_not_writable403The application is shared; an organization cannot edit it.
git_app_sync_failed, git_app_sync_unsupported502 / 400The provider refused the sync, or sync is GitHub-only.
installation_claimed_by_another_organization409Another organization already connected this installation.
install_authorization_required, install_not_authorizedredirect (?error=)The GitHub callback arrived without its authorization code, or the authorizing user cannot see the installation.
invalid_request, state_invalid, forbidden, conversion_failed, create_failed, unavailableredirect (?error=)Callback-flow failures: missing parameters, a stale or tampered state, the wrong organization, or the App could not be created.
source_url_conflict409A repository with this canonical URL already exists in the organization.
source_repository_url_invalid, source_repository_url_must_be_https400The URL is malformed or not https.
source_ssh_requires_credential400An SSH URL needs a deploy key.
source_scope_immutable, source_scope_not_supported400A repository's scope cannot change, or is not offered for this provider.
source_credential_not_supported, source_credential_provider_mismatch, source_installation_provider_mismatch400The credential or installation does not fit the repository's provider.
source_refresh_not_supported400Only provider-connected repositories refresh.
source_not_visible_to_connection404The connection can no longer see the repository — removed from the installation, or access revoked.
source_read_failed, git_provider_request_failed502 (404 / 409 when the provider said so)The provider or the daemon-lane read failed.
ref_required400The repository records no default branch; name a ref.
invalid_list_path400listPath must be a relative path.
no_daemon_available503A plain-git repository is read through a connected server, and none is available.
source_referenced_by_compose409Detach the source from every service before deleting the repository.
source_ref_unresolved422 (deploy)The bound branch does not exist, or the provider refused the lookup.
Edit on GitHub

Last updated on

On this page