---
title: "GitHub"
description: "Deploy a branch or commit without a checkout, and every push"
order: 9
---

Connect GitHub once, link a repo to an app, and moin deploys from GitHub: any branch, tag or commit from the web, the CLI, the terminal UI or an agent, with no checkout on your machine. A ref is any of these three: a branch like `main`, a tag like `v1.4.0` or a commit like `3f2a9c1`. With **Deploy every push** on, every push to the branch deploys. moin writes how it went back to GitHub as the commit status **moin/deploy**, so the pull request shows a green check when the deploy is live.

## Connect GitHub

Installs the moin GitHub App on your account or an organization, so moin may read the repos you choose. You do it once per workspace.

- Web: the app page, **GitHub**, **Connect GitHub**
- CLI: `moin github`
- TUI: opens the web: `w`, then `g`
- API: `GET /api/v1/github`
- MCP: `get_github`

The CLI, the API and MCP give you the link and the TUI opens it (`w`, then `g`); connecting always happens on github.com. GitHub sends you back and the repos show up. The same command shows where the app is installed.

Already installed the app? Click **Connect GitHub** all the same. moin asks GitHub which accounts have the app and connects them. If none has it yet, GitHub asks you to install it. An account connected to another workspace stays there: disconnect it there first.

To add another account or organization later, the CLI, the API and MCP give a link that installs the app there.

The app asks for the least it needs: read the contents of the repos you choose, and write commit statuses. It cannot push code.

## See the repos

Lists the repos moin may read, and where on GitHub you give it another one.

- Web: the app page, **GitHub**
- CLI: `moin github repos`
- TUI: on the app, `W` opens its GitHub panel in the web
- API: `GET /api/v1/github/repos`
- MCP: `list_repos`

In the TUI, the app's header shows the repo and the branch it follows, and `B` deploys from it. Linking is setup: `W` opens the app's GitHub panel in the web.

If you don't see your repo, moin may not read it yet. Under the list, every surface links to the settings page of each account on github.com where you choose the repos moin may read, and gives the link that installs the app on another account or organization. Add the repo there and come back: the web refreshes the list with **Refresh**, the others when you list again. With no repo yet, that link is the first step.

The branches of a repo, the default branch first:

- Web: the app page, **GitHub**, the **Branch** select after you choose the repo
- CLI: `moin github branches <owner>/<repo>`
- TUI: on the app, `W` opens its GitHub panel in the web
- API: `GET /api/v1/github/repos/{owner}/{repo}/branches`
- MCP: `list_branches`

## Link a repo

Makes an app follow a repo and a branch. Deploy every push is on by default. A branch or ref name follows git's rules and has at most 255 characters; moin refuses others, like one that starts with `-` or has `..`. moin also refuses a branch the repo does not have on GitHub: [list its branches](#see-the-repos) to see them. The app needs no deploy before: add it, link the repo and deploy it from GitHub.

- Web: the app page, **GitHub**, choose the repo and the branch, **Deploy**
- CLI: `moin link <owner>/<repo> --branch main`
- TUI: on the app, `W` opens its GitHub panel in the web
- API: `PUT /api/v1/apps/{app}/repo`
- MCP: `link_repo`

In the web, **Deploy** links the repo and deploys the branch in one step. The CLI, the API and MCP link first and then deploy, as below.

## Deploy from GitHub

Deploys the linked branch, or any branch, tag or commit, without a checkout on your computer.

- Web: the app page, **GitHub**, type a branch, tag or commit or leave it empty, **Deploy**
- CLI: `moin deploy --ref <ref>`
- TUI: on the app, `B`, type a branch, tag or commit or nothing, then `enter`
- API: `POST /api/v1/apps/{app}/deploys`
- MCP: `deploy_ref`

Empty means the linked branch. In the API, send `{"ref": "v1.4.0"}`, or `{"ref": ""}` for the linked branch.

## Deploy every push

Deploys each push to the linked branch by itself.

- Web: the app page, **GitHub**, **Deploy every push**
- CLI: `moin link <owner>/<repo> --auto=false`
- TUI: on the app, `W` opens its GitHub panel in the web
- API: `PUT /api/v1/apps/{app}/repo`
- MCP: `link_repo`

These turn it off; `moin link <owner>/<repo>` turns it on again. In the API and MCP it is `auto_deploy`. Linking the repo and branch the app follows already changes only this setting, without asking GitHub; the switch on the web does the same.

A push to another branch does nothing. A push that arrives while a deploy of the app still runs is skipped; the next push, or **Deploy**, picks up the newest commit.

## Unlink

Stops the app from following the repo. The app keeps running.

- Web: the app page, **GitHub**, **Unlink**
- CLI: `moin unlink`
- TUI: on the app, `W` opens its GitHub panel in the web
- API: `DELETE /api/v1/apps/{app}/repo`
- MCP: `unlink_repo`

## How a deploy from GitHub runs

1. moin resolves the ref to a commit: `main` becomes `5ee5ee5`.
2. Your server downloads that commit straight from GitHub, with a short-lived token the control plane gets for it. The code never passes through moin.
3. Your server builds and starts it the same way, with a snapshot of the database first.
4. The commit gets the status **pending**, then **success** or **failure** with a link to the app.

The deploy list shows the commit and its message, in the web, the TUI and the API.

## Without the GitHub App

[`moin deploy`](/docs/guides/deploy) uploads the directory you are in through [your bucket](/docs/guides/bucket), and [CI](/docs/guides/ci) can build an image and call a deploy hook. Both work without connecting GitHub.

On a self-hosted control plane, deploys from GitHub need [your own GitHub App](/docs/self-hosting#your-own-github-app).
