Why can't your organization fork an open source repo and send a pull request? Often, policy prevents it due to credential leaks, IP, PII, and compliance reviews. GitHub Private Mirrors allow private work while exposing only approved changes publicly. This post explains what GitHub Private Mirrors are, why they exist, how they work, and their relation to your org, upstream, and tools like the [GitHub Private Mirrors App][pma-repo].

## What Are GitHub Private Mirrors?

A private mirror is a private copy of a public repository within your organization. You develop and review there; when ready, push approved work to a public fork in your org, then open a pull request to the upstream project. The mirror remains private, with only synced content leaving your control.

The [GitHub Private Mirrors App][pma-repo] (PMA) is a self-hosted GitHub App with a web UI that automates creating private mirrors of your org's public forks and manages syncs of approved branches from mirror to fork for upstream PRs. It acts like a secure airlock, keeping work in the mirror until merged to the default branch, then syncing that branch to the public fork. It doesn't store your code with any third party; it only runs Git operations between repos you control, maintaining commit history, authors, and signatures without rewriting commits.

## Why GitHub Private Mirrors Exist

Many organizations want to contribute upstream but can't allow "anyone to fork and open" a PR due to security and legal issues. They may need to verify every line for approval or forbid pushing from developer machines to public remotes. Risks include credential commits, internal IPs, PII in logs or configs, or unreviewed code, hindering workflow.

Private mirrors keep development in private repos, syncing selected branches to a public fork for approval before opening a PR. Internal review and compliance remain private; only approved content becomes public. They enable upstream contributions where direct fork-and-PR isn't permitted until work is approved.

## How GitHub Private Mirrors Work

Flow: upstream (public) → your org's public fork → your org's private mirror(s). Develop in a mirror; merging syncs the default branch to the public fork. Then, open a PR from the fork to upstream. The app only links your fork and mirrors, never contacts upstream.

```mermaid
flowchart TB
  subgraph upstream["Upstream (public)"]
    U[Upstream repo]
  end
  subgraph your_org["Your organization"]
    PF[Public fork]
    PM[Private mirror]
  end
  U -->|fork| PF
  PF -->|PMA creates| PM
  PM -->|develop & merge| PM
  PM -->|PMA syncs branch| PF
  PF -->|you open PR| U
```

PMA manages GitHub webhooks, calls APIs, creates mirrors, manages branches, and triggers syncs without storing source code. Run on your infrastructure or cloud to receive HTTPS webhooks. One instance supports one organization; multiple orgs need multiple instances and separate app installations.

## How Do GitHub Private Mirrors App, Your Org, and Upstream Relate?

* **PMA and your org:** The app manages repos and branches in orgs via GitHub API and webhooks. Your code remains on GitHub, in forks and mirrors, not in the app.
* **PMA and upstream:** PMA never contacts upstream; you open PRs from your fork to upstream. The app only syncs the fork and mirror.
* **PMA and GitHub Enterprise Cloud:** PMA connects to GitHub.com or GHEC using your GHEC org(s). The UI logs in with a GitHub.com account but works on GHEC org repos.

## GitHub Private Mirrors App Trade-offs and Limitations

* **Self-hosted only:** No hosted SaaS; you run and operate the app and hosting yourself.
* **One instance per org:** One deployment covers one GitHub or GitHub Enterprise Cloud org; multiple orgs or instances require additional deployments and app installs.
* **Pre-1.0:** The project is in public beta; behavior and configuration may change. Check the [repo][pma-repo] and docs for updates.
* **Webhook and URL:** GitHub mandates HTTPS and a reachable URL for webhooks. Even with locked-down apps using Identity and Access Management, the webhook endpoint must be accessible by GitHub (via a public URL or load balancer).

## GitHub Private Mirrors App Common Misconceptions

* **"The app stores my code."** It triggers syncs, creating or updating repos and branches via the GitHub API. Your code stays in your org's GitHub repos (public forks and private mirrors).
* **"One app can serve many orgs."** One app installation and running instance serves one org or one GitHub Enterprise Cloud org. Additional orgs or instances need more deployments.
* **"I can use it without self-hosting."** There is no hosted offering today; you run the app yourself (on Cloud Run, another cloud, or on-prem). The [project tracks interest][pma-issue-122] in a hosted offering.

## Deploy GitHub Private Mirrors App on AWS Lambda (minimal steps)

Run PMA on AWS Lambda with one ECR repo, a container image (PMA + Lambda Web Adapter), and a Lambda Function URL for the webhook and UI. Details are in your OpenTofu or deployment repo.

1. **Prerequisites:** AWS account, Docker, [OpenTofu](https://opentofu.org) (the `tofu` command), and a [GitHub App][developing-docs] (org Settings → Developer settings → GitHub Apps → New). Note the App ID, client ID, client secret, private key (PEM), and generate a webhook secret and NextAuth secret.
2. **Get the deployment config:** Use an OpenTofu module or repo to define an ECR repository, a Lambda function (Image), and a Lambda Function URL. The image should be the official PMA image with [Lambda Web Adapter](https://github.com/awslabs/aws-lambda-web-adapter) so the Next.js app can serve via the Function URL.
3. **Set variables:** In a `.tfvars` file, set `region`, GitHub App credentials, secrets, `public_org`, `private_org`, and placeholders for `nextauth_url` (set after first deploy). Optionally, set `build_image = false` to use a published PMA image instead of building from source.
4. **First apply:** Run `tofu init` and `tofu apply` (with your var file). OpenTofu creates the ECR repo, builds or pulls the image, pushes it, creates the Lambda and Function URL, and outputs the URL.
5. **Point the GitHub App at the app:** Set the app’s Homepage and Webhook URLs to the Function URL (and `/api/webhooks` for the webhook). Use the callback path (e.g., `…/api/auth/callback/github`) for User authorization callback URL, and include the same Function URL (with trailing slash) as `nextauth_url` in your config.
6. **Second apply:** Re-run `tofu apply` to update the Lambda with the correct `nextauth_url`. Then, open the Function URL, install the app on the org, and use PMA to create and sync mirrors.

Use OpenTofu outputs or the AWS CLI (`aws lambda get-function-configuration --query CodeSha256`) to check the deployed image by comparing source image URL, deployed image URI, or ECR image digests.

## GitHub Private Mirrors App Conclusion

Private mirrors provide a private workspace: development and review occur in the mirror, and only approved work syncs to a public fork for PR. This separation exists because many orgs can't allow direct fork-and-PR until internal checks pass. The GitHub Private Mirrors App automates mirror-and-sync, managing mirrors and branch syncs without storing code. Each instance serves one org, and you run it yourself. Use this to decide whether private mirrors suit your needs and where to find setup and usage instructions.

## GitHub Private Mirrors App Next Steps

* [GitHub Private Mirrors repo][pma-repo]: README, source, and issue tracker.
* [Developing the Private Mirrors App][developing-docs]: GitHub App setup, permissions, and events.
* [Using the Private Mirrors App][using-the-app]: Creating forks, mirrors, and the PR workflow.
* [Deploy containerized applications to Cloud Run][cloud-run-deploy]: Google Cloud guide for building and deploying containers to Cloud Run.
* To try the same stack locally without AWS, use [LocalStack](https://localstack.cloud) and OpenTofu (see your deployment repo’s LocalStack test, if present; use `TF_CMD=tofu tflocal` with the terraform-local wrapper; Lambda container image support may require LocalStack Pro).

## GitHub Private Mirrors App References

* [GitHub Private Mirrors][pma-repo], the official repository and README.
* [Developing the Private Mirrors App][developing-docs] and [Using the Private Mirrors App][using-the-app], for setup and usage.
* [Deploy a containerized application to Cloud Run][cloud-run-deploy], for Cloud Run deployment.
* [Interest in a hosted Private Mirrors offering][pma-issue-122], GitHub issue.

[pma-repo]: https://github.com/github-community-projects/private-mirrors
[developing-docs]: https://github.com/github-community-projects/private-mirrors/blob/main/docs/developing.md
[using-the-app]: https://github.com/github-community-projects/private-mirrors/blob/main/docs/using-the-app.md
[cloud-run-deploy]: https://cloud.google.com/build/docs/deploy-containerized-application-cloud-run
[pma-issue-122]: https://github.com/github-community-projects/private-mirrors/issues/122