---
title: "Git repositories"
description: "Build a Git repository with its Dockerfile or with Railpack, from public URLs or private GitHub and GitLab repositories."
canonical_url: "https://harblet.com/docs/deploy/git-repositories"
---
# Git repositories

> Build a Git repository with its Dockerfile or with Railpack, from public URLs or private GitHub and GitLab repositories.

A Git service builds your repository on every deploy and runs the result. Repositories with a Dockerfile build from it. Repositories without one build through [Railpack](https://github.com/railwayapp/railpack), which detects the language and framework and creates an image for you.

## Create a Git service

<steps level="3">

### Open the project

Open a project and click **New service** → **Git repository**.

### Fill in the form

<field-group>
<field name="Name" :required="true">

Up to 64 characters, for example `Web`.

</field>

<field name="Repository" :required="true">

Choose a repository from a Git connection, or enter a public `https://` URL. See [Private repositories](#private-repositories).

</field>

<field name="Branch" :required="true">

The branch to build. Defaults to `main`. Choosing a repository from a Git connection fills in its default branch.

</field>

<field name="Root directory">

Where your app lives in the repository, for example `apps/web` in a monorepo. Leave it empty for the top level.

</field>

<field name="Dockerfile path">

The Dockerfile to build, relative to the root directory. Leave it empty to use a `Dockerfile` in the root directory if there is one, or Railpack if there isn't.

</field>

<field name="Port">

The port your app listens on, from 1 to 65535. Harblet passes it to your app as `PORT`. Leave it empty if the service doesn't take requests, such as a worker.

</field>
</field-group>

### Create it

Click **Create service**. Harblet starts the first build right away.

</steps>

Follow the build on the service's **Deployments** tab. See [Deployments and logs](https://harblet.com/docs/deploy/deployments).

## Public repositories

Any public repository works through its `https://` URL, from GitHub, GitLab, Codeberg or any other host:

```text
https://github.com/owner/repository
```

Harblet clones it without credentials. URLs with a username or password, SSH URLs and other protocols aren't accepted.

## Private repositories

Private repositories need a **Git connection**. Harblet has two kinds:

- **GitHub** through a GitHub App that Harblet creates for you. The app can only read the repositories you choose.
- **GitLab** through an access token that you create, on GitLab.com or on a GitLab you host yourself.

<note>

Adding and removing Git connections is for admins. Once a connection is added, every member can choose its repositories.

</note>

### Connect GitHub

<steps level="4">

#### Start on Harblet

Go to **Settings** → **Git**, click **Connect** and choose **GitHub**.

- For your **personal account**, leave **Organization** empty.
- For an **organization**, enter its name as it appears in its GitHub URL, for example `acme` for `github.com/acme`. You need to be allowed to create apps in that organization.

Click **Continue on GitHub**.

#### Create the app on GitHub

GitHub shows the app Harblet asks for: a private app named `harblet-…` that can read the contents and metadata of repositories. Confirm it.

#### Install the app

GitHub then asks where to install the app. Choose **All repositories** or **Only select repositories**, then install it. GitHub sends you back to **Settings** → **Git**.

</steps>

The connection now shows under **Settings** → **Git**. Its **Access** column shows **All repositories** or **Selected repositories**. Click **Choose repositories** to change them on GitHub.

If you left GitHub before installing the app, the connection shows **Not installed**. Click **Install** to finish.

### Connect GitLab

<steps level="4">

#### Create an access token on GitLab

Create an access token with the scopes `read_api` and `read_repository`:

- A **personal access token** reads every project you're a member of.
- A **group access token** or a **project access token** reads only that group or project. Give it the **Reporter** role.

#### Connect it

Go to **Settings** → **Git**, click **Connect** and choose **GitLab**.

<field-group>
<field name="GitLab URL" :required="true">

Keep `https://gitlab.com` for GitLab.com. If you host GitLab yourself, enter its address, for example `https://gitlab.example.com`, with `https://` and without a path.

</field>

<field name="Access token" :required="true">

The token you created.

</field>
</field-group>

Click **Connect**. Harblet checks the token with GitLab before it saves the connection.

</steps>

The connection shows under **Settings** → **Git** with the token's name. **Account** shows your username for a personal access token, or the group or project for a group or project access token. **Access** shows when the token expires, with **Expires soon** in its last 30 days.

<note>

Your servers have to reach your GitLab over HTTPS, since builds clone from it.

</note>

<table>
<thead>
  <tr>
    <th>
      Message
    </th>
    
    <th>
      What to do
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      GitLab doesn't accept this token. Check that you copied all of it and that it hasn't expired.
    </td>
    
    <td>
      Copy the token again, or create a new one.
    </td>
  </tr>
  
  <tr>
    <td>
      The token needs the scopes read_api and read_repository.
    </td>
    
    <td>
      Create a new token with both scopes.
    </td>
  </tr>
  
  <tr>
    <td>
      Harblet can't reach GitLab at ….
    </td>
    
    <td>
      Check the <strong>
        GitLab URL
      </strong>
      
      , and that your server reaches it over HTTPS.
    </td>
  </tr>
</tbody>
</table>

### Replace a GitLab token

Harblet doesn't renew tokens. Before a token expires, create a new one on GitLab. Then click **Replace token** next to the connection under **Settings** → **Git**, enter the new token and click **Replace token**.

The services that use the connection keep it, and their next build clones with the new token. The new token needs the same scopes and has to come from the same GitLab.

A token that expired or was revoked shows **Expired or revoked**. Builds of its private repositories fail until you replace it.

### Choose a repository

The **Repository** field in **New service from Git** and in a service's **Settings** lists every repository the Git connections can read, grouped by connection, for example **GitHub · acme** or **GitLab · acme**. Private repositories have a lock. Choosing one also sets the branch to the repository's default branch.

To enter a URL instead, click **Use a public URL**. Click **Choose a repository** to go back to the list.

A connection that Harblet can't reach shows **Unreachable** under **Settings** → **Git**. Hover over it for the reason.

<tip>

One connection serves all projects. Connect each GitHub account or organization, or each GitLab token, once.

</tip>

### Remove a connection

Under **Settings** → **Git**, click the trash button of the connection, then **Remove connection**. Services that used it clone without credentials from then on, so builds of private repositories fail.

- **GitHub:** Harblet uninstalls the app from the account and forgets it. The app itself stays on GitHub. Delete it there in the app's settings under **Advanced**.
- **GitLab:** Harblet forgets the token. The token stays valid on GitLab. Revoke it there if nothing else uses it.

## How Harblet builds

Every build runs these steps:

1. **Clone.** Harblet clones the newest commit of the branch. The build output names the commit.
2. **Build.** Harblet builds the root directory:

  - with the Dockerfile at **Dockerfile path**, if you set one,
  - otherwise with a `Dockerfile` in the root directory, if there is one,
  - otherwise with Railpack.
3. **Deploy.** Harblet stores the image in its own registry and deploys it.

### Railpack

Railpack finds out how to build and start your app from its files, for example a `package.json`, `requirements.txt` or `go.mod`. If it can't find a start command, the build fails. Configure Railpack with a `railpack.json` in the root directory or with environment variables, as described in the [Railpack documentation](https://railpack.com).

### Variables during the build

All of the service's [environment variables](https://harblet.com/docs/deploy/environment-variables) reach the build too:

- **Railpack** gets them as environment variables and as build secrets.
- **Dockerfiles** get them as build arguments and as build secrets. Read them with `ARG`, or mount them as a secret:

```dockerfile [Dockerfile]
ARG NEXT_PUBLIC_API_URL
RUN --mount=type=secret,id=NPM_TOKEN NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN) npm ci
```

Prefer the secret mount for credentials, since build arguments can end up in the image's history.

### The `PORT` variable

When your Git service has a port, Harblet sets `PORT` to it while the service runs, unless you set `PORT` yourself. Make your app listen on `PORT`. `PORT` isn't set during the build.

### Build limits

- **30 minutes.** Builds that take longer fail with "The build took longer than 30 minutes."
- **x86_64 and arm64.** Railpack builds run on these architectures only.
- **The last five builds** of each service stay in Harblet's registry. Older builds may no longer be available to [redeploy](https://harblet.com/docs/deploy/deployments#redeploy-an-earlier-deployment).

## Deploy on every push

Harblet doesn't deploy on push by itself yet. Call the service's [deploy hook](https://harblet.com/docs/deploy/deploy-hooks) at the end of your CI pipeline instead. The build then takes the newest commit of the branch.

## Build errors

<table>
<thead>
  <tr>
    <th>
      Message
    </th>
    
    <th>
      What to do
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      There is no directory … in the repository.
    </td>
    
    <td>
      Check <strong>
        Root directory
      </strong>
      
       under <strong>
        Settings
      </strong>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      There is no … in the repository.
    </td>
    
    <td>
      Check <strong>
        Dockerfile path
      </strong>
      
       under <strong>
        Settings
      </strong>
      
      . It's relative to the root directory.
    </td>
  </tr>
  
  <tr>
    <td>
      The build failed. See the build logs.
    </td>
    
    <td>
      Open the deployment on the <strong>
        Deployments
      </strong>
      
       tab and read the <strong>
        Build
      </strong>
      
       output.
    </td>
  </tr>
  
  <tr>
    <td>
      The GitHub App … can't access …. Choose its repositories on GitHub.
    </td>
    
    <td>
      Under <strong>
        Settings
      </strong>
      
       → <strong>
        Git
      </strong>
      
      , click <strong>
        Choose repositories
      </strong>
      
       and add the repository.
    </td>
  </tr>
  
  <tr>
    <td>
      The GitLab access token … can't access …. It needs at least the Reporter role there.
    </td>
    
    <td>
      Give the token at least the <strong>
        Reporter
      </strong>
      
       role in that project, or choose the repository from a connection that can read it.
    </td>
  </tr>
  
  <tr>
    <td>
      GitLab rejected the access token …. It expired or was revoked, so an admin has to replace it in Settings → Git.
    </td>
    
    <td>
      Replace the token, see <a href="#replace-a-gitlab-token">
        Replace a GitLab token
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      … isn't a repository on ….
    </td>
    
    <td>
      The repository's address doesn't match the connection's <strong>
        GitLab URL
      </strong>
      
      . Choose the repository again from the list.
    </td>
  </tr>
  
  <tr>
    <td>
      The build took longer than 30 minutes.
    </td>
    
    <td>
      Speed up the build, for example with a smaller build context or a Dockerfile with cached layers.
    </td>
  </tr>
</tbody>
</table>


## Sitemap

See the full [sitemap](https://harblet.com/sitemap.md) for all pages.
