# Mirror a GitHub repository

A mirror is a read-only copy of a repository that syncs from its source on a schedule. Code, branches, and tags stay up to date. If the source goes offline, the mirror keeps serving.

This is not the same as a one-time import. An import creates an independent repo and cuts ties to the source. A mirror stays connected and pulls changes automatically. Use a mirror when you want a live copy; use an [import](/docs/migrate-from-github/) when you are moving permanently.

## Prerequisites

- A Codebahn account with an organization
- Access to the GitHub repos you want to mirror (via the GitHub App or a personal access token)

## Set up a mirror

There are three ways to create a mirror: the GitHub App (recommended), a personal access token, or the [`codebahn` CLI](/docs/cli/).

### Option A: GitHub App (recommended)

No token to manage. Codebahn mints a short-lived, repo-scoped token on each sync. Nothing is stored except the installation ID, which is not a secret.

1. Log in to Codebahn

2. Click **+** > **Import repository** > **GitHub**

3. On the **Connect GitHub** tab, click **Connect GitHub account**

4. Authorize the Codebahn app on GitHub. If this is your first time, install it on your account or organization and grant access to the repos you want to mirror

5. Check **This repository will be a mirror**. Metadata checkboxes (issues, PRs, etc.) are disabled because mirrors are git-only

6. Select the repos to mirror

7. Choose an owner and click **Import selected**

The 30-minute connection window applies to the initial setup only. Once the mirror is created, syncs run unattended.

### Option B: Personal access token

If you cannot install the GitHub App (e.g. an account where you lack admin rights), use a token instead.

1. [Create a fine-grained personal access token](/docs/migrate-from-github/#create-a-github-personal-access-token) with Contents: Read-only. Set a long expiration (90 days or the maximum your org allows). If the token expires, syncs stop until you update it in mirror settings

2. Click **+** > **Import repository** > **GitHub**

3. Switch to the **Personal access token** tab

4. Paste the HTTPS clone URL, e.g. `https://github.com/your-org/your-repo.git`

5. Paste the token in the **Access token** field

6. Check **This repository will be a mirror**

7. Choose an owner and confirm the repo name

8. Click **Migrate repository**

You can convert a token mirror to the GitHub App later from the mirror's settings page, which removes the stored token.

### Option C: CLI

The [`codebahn` CLI](/docs/cli/) can set up mirrors from your terminal. [Install the CLI](/docs/cli/#install) first.

Preview which repos are accessible before mirroring:

```bash
codebahn repo list-remote github.com/your-org
```

Mirror a single repo:

```bash
codebahn repo mirror github.com/your-org/your-repo
```

Without `--source-token`, the CLI authenticates through the Codebahn GitHub App device flow (opens your browser, no token to manage). To use a personal access token instead:

```bash
codebahn repo mirror github.com/your-org/your-repo --source-token $GITHUB_TOKEN
```

Mirror an entire org:

```bash
codebahn repo mirror github.com/your-org --owner my-codebahn-org -y
```

Use `--include` and `--exclude` to select repos:

```bash
codebahn repo mirror github.com/your-org --include "api-*" --exclude "archived-*" -y
```

## What syncs

Mirrors pull all Git refs: branches, tags, and commits. Every sync is a full `git remote update`.

Mirrors are git-only. Issues, pull requests, labels, milestones, and releases are not imported, even if those options appear in the form. If you need issue history alongside the mirror, do a one-time [import](/docs/migrate-from-github/) first, then delete it and create the mirror into the same name.

## Sync interval

The default sync interval is eight hours. You can change it per repo:

1. Go to **Settings** > **Repository** on the mirror repo

2. Under **Mirror Settings**, set the interval (minimum ten minutes)

3. Click **Save**

A shorter interval means fresher data but more traffic to GitHub. For active repos where you want near-real-time redundancy, 15 to 30 minutes works well.

You can also click **Synchronize Now** on the mirror settings page to trigger an immediate sync.

## Change authentication method

You can convert a token-based mirror to the GitHub App without deleting and re-importing. Stars, forks, webhooks, and the repo URL are preserved.

### Switch from token to GitHub App

1. Go to **Settings** > **Repository** on the mirror repo

2. Under **Mirror Settings**, click **Connect GitHub App**

3. Complete the GitHub authorization flow

4. The stored token is removed. Future syncs mint short-lived tokens automatically

### Reconnect a broken GitHub App mirror

If you uninstall the Codebahn app from GitHub or change its repository access, syncs fail with a notice. To fix it:

1. Reinstall the app on GitHub (or update its repository access)

2. Go to **Settings** > **Repository** on the mirror repo

3. Click **Reconnect GitHub App**

4. Authorize on GitHub

## Working with a mirror

A mirror repo looks and works like any other Codebahn repo for reading: browse code, view commit history, compare branches, download archives. CI workflows do not run on mirror repos.

The one difference: you cannot push to a mirror. Git operations are read-only. If you clone the mirror locally and try to push, you get a rejection.

## If the source goes down

When GitHub is unreachable, mirror syncs fail and retry on the next interval. The mirror keeps serving whatever it had at the last successful sync.

If you need to push (because GitHub is down and work cannot wait), convert the mirror to a regular repository:

1. Go to **Settings** > **Repository** on the mirror repo

2. Scroll to **Danger Zone**

3. Click **Convert to regular repository**

4. Confirm

This removes the mirror link and makes the repo writable. You can push, open pull requests, and run CI. Your `.github/workflows/` files run on Codebahn's runners without changes. See [CI workflows](/docs/migrate-from-github/#ci-workflows) for what works and what to watch for.

:::caution[One-way conversion]
Converting to a regular repository is permanent. There is no "re-attach as mirror" action. To go back to mirroring, delete the repo and create a new mirror.

In practice, this is fine for the most common scenario: GitHub is down, you need to keep working, and when it comes back you can reconcile by pushing your changes back to GitHub.
:::

## After converting: reconcile with GitHub

When GitHub comes back online, you will have commits on Codebahn that GitHub does not have. To reconcile:

```bash
# Add both remotes
git remote add codebahn git@codebahn.net:your-org/your-repo.git
git remote add github git@github.com:your-org/your-repo.git

# Pull from Codebahn (where you pushed during the outage)
git pull codebahn main

# Push to GitHub
git push github main
```

If both sides have new commits (someone pushed to GitHub after it recovered, before you reconciled), you will need to merge or rebase as with any diverged branch.

## Mirror an entire organization

**CLI:** mirror an entire org in one command. See [Option C: CLI](#option-c-cli) above.

**API:** script it with the migration API and a personal access token:

```bash
curl -X POST "https://codebahn.net/api/v1/repos/migrate" \
  -H "Authorization: token YOUR_CODEBAHN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "clone_addr": "https://github.com/your-org/repo-name.git",
    "auth_token": "YOUR_GITHUB_TOKEN",
    "repo_name": "repo-name",
    "repo_owner": "your-codebahn-org",
    "service": "github",
    "mirror": true
  }'
```

The GitHub App flow does not support the migration API. For scripted bulk mirrors, use a token.

## Limitations

- **Issues and PRs are not included.** Mirrors import git refs only. No issues, PRs, labels, milestones, or releases.
- **Wiki is not mirrored.** Codebahn does not enable wikis.
- **LFS objects sync if configured.** Set the LFS endpoint in mirror settings if the source repo uses Git LFS.
- **Token mirrors: the token must stay valid.** If it expires or is revoked, syncs fail. Update the token in **Settings** > **Repository** > **Mirror Settings**. Consider converting to the GitHub App to avoid token rotation entirely.
- **GitHub App mirrors: the app must stay installed.** If you uninstall the Codebahn app from GitHub, syncs fail. Reinstall the app and click **Reconnect** in mirror settings.
- **CI does not run on mirror repos.** Convert to a regular repo first. After conversion, `.github/workflows/` files run on Codebahn's runners.
