Gitea Mirror auto-syncs GitHub repos to your self-hosted Gitea/Forgejo, with a sleek Web UI and easy Docker deployment.
1,434
stars
789
commits
TypeScript
primary language
Sep 9, 2026
updated
Automatically mirror repositories from GitHub to your self-hosted Gitea/Forgejo instance.
# Fastest way - using the simplified Docker setup
docker compose -f docker-compose.alt.yml up -d
# Access at http://localhost:4321
First user signup becomes admin. Configure GitHub and Gitea/Forgejo through the web interface!
π Full documentation - quickstart, deployment, configuration, notifications, SSO and more. Offline copies live in docs/.
GH_API_URL
We provide two Docker Compose options:
Perfect for trying out Gitea Mirror or simple deployments:
# Clone repository
git clone https://github.com/RayLabsHQ/gitea-mirror.git
cd gitea-mirror
# Start with simplified setup
docker compose -f docker-compose.alt.yml up -d
# Access at http://localhost:4321
Features:
./data directoryBest for:
For production deployments with environment-based configuration:
# Start with full configuration options
docker compose up -d
Features:
Best for:
docker pull ghcr.io/raylabshq/gitea-mirror:latest
Minimal .env file (optional - has sensible defaults):
# Custom port (default: 4321)
PORT=4321
# User/Group IDs for file permissions (default: 1000)
PUID=1000
PGID=1000
# Session secret (auto-generated if not set)
BETTER_AUTH_SECRET=your-secret-key-change-this-in-production
All other settings are configured through the web interface after starting.
Supports extensive environment variables for automated deployment. See the full docker-compose.yml for all available options including GitHub tokens, Gitea URLs, mirror settings, and more.
π For a complete list of all supported environment variables, see the Environment Variables Documentation.
# One-line install on Proxmox VE
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/gitea-mirror.sh)"
See the Proxmox VE Community Scripts for more details.
Zero-configuration deployment with Nix:
# Run immediately - no setup needed!
nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror
# Or build and run locally
nix build --extra-experimental-features 'nix-command flakes'
./result/bin/gitea-mirror
# Or install to profile
nix profile install --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror
gitea-mirror
NixOS users - add to your configuration:
{
inputs.gitea-mirror.url = "github:RayLabsHQ/gitea-mirror";
services.gitea-mirror = {
enable = true;
betterAuthUrl = "https://mirror.example.com";
openFirewall = true;
};
}
Secrets auto-generate, database auto-initializes. See NIX.md for quick reference or docs/NIX_DEPLOYMENT.md for full documentation.
# Install Bun
curl -fsSL https://bun.sh/install | bash
# Setup and run
bun run setup
bun run dev
First Time Setup
Mirror Strategies
Customization
Mirror Git LFS objects along with your repositories:
LFS_START_SERVER = true)Transfer complete repository metadata from GitHub to Gitea/Forgejo:
Enable in Settings β Mirror Options β Mirror metadata
Gitea Mirror provides powerful automatic synchronization features:
SCHEDULE_ENABLED=true or GITEA_MIRROR_INTERVAL is set - no manual clicks required!Navigate to the Configuration page and enable "Automatic Syncing" with your preferred interval.
π Set it and forget it! With these environment variables, Gitea Mirror will automatically:
# Option 1: Enable automatic scheduling (triggers auto-start)
SCHEDULE_ENABLED=true
SCHEDULE_INTERVAL=3600 # Check every hour (or use cron: "0 * * * *")
# Option 2: Set mirror interval (also triggers auto-start)
GITEA_MIRROR_INTERVAL=8h # Every 8 hours
# Other examples: 5m, 30m, 1h, 24h, 1d, 7d
# Advanced: Use cron expressions for specific times
SCHEDULE_INTERVAL="0 2 * * *" # Daily at 2 AM (optimize bandwidth usage)
# Auto-import new repositories (default: true)
AUTO_IMPORT_REPOS=true
# Auto-cleanup orphaned repositories
CLEANUP_DELETE_IF_NOT_IN_GITHUB=true
CLEANUP_ORPHANED_REPO_ACTION=archive # 'archive' (recommended) or 'delete'
CLEANUP_DRY_RUN=false # Set to true to test without changes
Important Notes:
SCHEDULE_ENABLED=true or GITEA_MIRROR_INTERVAL is set, the service automatically imports all GitHub repositories and mirrors them on startup. No manual "Import" or "Mirror" button clicks required!GITEA_MIRROR_INTERVAL determines how often each repository is actually synced. For example, with 8h, each repo syncs every 8 hours from its last successful sync.5m). Start with a longer interval (1h to 8h) or temporarily disable scheduling during the initial import/mirror run, then enable your regular interval after the first pass completes.π‘οΈ Backup Protection Features:
archive action preserves all repository data:
archived- prefix (Gitea/Forgejo API limitation prevents archiving mirrors)Manual Sync from the Repositories page whenever you need fresh data.CLEANUP_ORPHANED_REPO_ACTION=archive (default) instead of deleteGitea Mirror works with non-github.com GitHub deployments. Point the client at your Enterprise API via the GH_API_URL environment variable:
# GitHub Enterprise Server (self-hosted)
GH_API_URL=https://ghe.example.com/api/v3
# GitHub Enterprise Cloud with data residency
GH_API_URL=https://api.TENANT.ghe.com
Standard GitHub Enterprise Cloud on github.com needs no override. Use a token issued by the target Enterprise instance for GITHUB_TOKEN.
If you run behind a reverse proxy on a subpath (for example https://git.example.com/mirror), configure:
# BASE_URL handles the path prefix β auth URLs stay as origin only
BASE_URL=/mirror
BETTER_AUTH_URL=https://git.example.com
PUBLIC_BETTER_AUTH_URL=https://git.example.com
BETTER_AUTH_TRUSTED_ORIGINS=https://git.example.com
# β Auth endpoints resolve to: https://git.example.com/mirror/api/auth/*
Notes:
BASE_URL sets the application path prefix.BETTER_AUTH_URL and PUBLIC_BETTER_AUTH_URL should be origin only (e.g. https://git.example.com). Do not include the base path β it is applied automatically from BASE_URL. Any path accidentally included is stripped.BETTER_AUTH_TRUSTED_ORIGINS should also contain origins only (no path).BASE_URL is runtime configuration, so prebuilt registry images can be reused across different subpaths.For existing pull-mirror repositories, changing the GitHub token in Gitea Mirror does not always update stored mirror credentials in Gitea/Forgejo for already-created repositories.
If sync logs show authentication failures (for example terminal prompts disabled), do one of the following:
If you enable metadata options (issues/PRs/labels/milestones/releases) after repositories were already mirrored:
sqlite3 data/gitea-mirror.db "UPDATE repositories SET metadata = NULL;"
This clears per-repository metadata completion flags so the next sync can re-run metadata import steps.
MIN_INTERVALGitea Mirror treats the interval configured in Configuration (or GITEA_MIRROR_INTERVAL) as the source of truth and applies it to mirrored repositories during sync.
If your Gitea/Forgejo server has mirror.MIN_INTERVAL set to a higher value (for example 24h) and Gitea Mirror is set lower (for example 8h), sync/mirror operations can fail when updating mirror settings.
To avoid this:
MIN_INTERVAL.# Install dependencies
bun install
# Run development server
bun run dev
# Run tests
bun test
# Build for production
bun run build
ENCRYPTION_SECRET environment variable for production deploymentsBETTER_AUTH_SECRET if not setIf email delivery is not configured, an admin with server access can reset a user password from the command line:
bun run reset-password -- --email=user@example.com --new-password='new-secure-password'
What this does:
Use this only from trusted server/admin environments.
Gitea Mirror supports multiple authentication methods. Email/password authentication is the default and always enabled.
The standard authentication method. First user to sign up becomes the admin.
Enable users to sign in with external identity providers like Google, Azure AD, Okta, Authentik, or any OIDC-compliant service.
Configuration:
https://accounts.google.com)Redirect URL for your provider:
https://your-domain.com/api/auth/sso/callback/{provider-id}
Need help? The SSO & OIDC guide now includes a working Authentik walkthrough plus troubleshooting tips. If you upgraded from a version earlier than v3.8.10 and see TypeError β¦ url.startsWith after the callback, delete the old provider and add it again using the Discover button (see #73 and #122).
Perfect for automatic authentication when using reverse proxies like Authentik, Authelia, or Traefik Forward Auth.
Environment Variables:
# Enable header authentication
HEADER_AUTH_ENABLED=true
# Header names (customize based on your proxy)
HEADER_AUTH_USER_HEADER=X-Authentik-Username
HEADER_AUTH_EMAIL_HEADER=X-Authentik-Email
HEADER_AUTH_NAME_HEADER=X-Authentik-Name
# Auto-provision new users
HEADER_AUTH_AUTO_PROVISION=true
# Restrict to specific email domains (optional)
HEADER_AUTH_ALLOWED_DOMAINS=example.com,company.org
How it works:
Example Authentik Configuration:
# In your reverse proxy configuration
proxy_set_header X-Authentik-Username $authentik_username;
proxy_set_header X-Authentik-Email $authentik_email;
proxy_set_header X-Authentik-Name $authentik_name;
Gitea Mirror can also act as an OIDC provider for other applications. Register OAuth applications in Settings β Authentication & SSO β OAuth Applications tab.
Use cases:
Pull requests cannot be created as actual PRs in Gitea/Forgejo due to API limitations. Instead, they are mirrored as enriched issues with comprehensive metadata.
Why real PR mirroring isn't possible:
How we handle Pull Requests: PRs are mirrored as issues with rich metadata including:
This approach preserves all important PR information while working within Gitea/Forgejo's API constraints. The PRs appear in the issue tracker with clear visual distinction and comprehensive details.
Contributions are welcome! Please read our Contributing Guidelines for details on our code of conduct and the process for submitting pull requests.
GNU Affero General Public License v3.0 (AGPL-3.0) - see LICENSE file for details.
Hacker News (1)
TypeScript
78.9%
Nix
8.6%
MDX
7.4%
Astro
2.5%
Shell
1.9%
Gitea Mirror auto-syncs GitHub repos to your self-hosted Gitea/Forgejo, with a sleek Web UI and easy Docker deployment.
1,434
stars
789
commits
TypeScript
primary language
Sep 9, 2026
updated
Automatically mirror repositories from GitHub to your self-hosted Gitea/Forgejo instance.
# Fastest way - using the simplified Docker setup
docker compose -f docker-compose.alt.yml up -d
# Access at http://localhost:4321
First user signup becomes admin. Configure GitHub and Gitea/Forgejo through the web interface!
π Full documentation - quickstart, deployment, configuration, notifications, SSO and more. Offline copies live in docs/.
GH_API_URL
We provide two Docker Compose options:
Perfect for trying out Gitea Mirror or simple deployments:
# Clone repository
git clone https://github.com/RayLabsHQ/gitea-mirror.git
cd gitea-mirror
# Start with simplified setup
docker compose -f docker-compose.alt.yml up -d
# Access at http://localhost:4321
Features:
./data directoryBest for:
For production deployments with environment-based configuration:
# Start with full configuration options
docker compose up -d
Features:
Best for:
docker pull ghcr.io/raylabshq/gitea-mirror:latest
Minimal .env file (optional - has sensible defaults):
# Custom port (default: 4321)
PORT=4321
# User/Group IDs for file permissions (default: 1000)
PUID=1000
PGID=1000
# Session secret (auto-generated if not set)
BETTER_AUTH_SECRET=your-secret-key-change-this-in-production
All other settings are configured through the web interface after starting.
Supports extensive environment variables for automated deployment. See the full docker-compose.yml for all available options including GitHub tokens, Gitea URLs, mirror settings, and more.
π For a complete list of all supported environment variables, see the Environment Variables Documentation.
# One-line install on Proxmox VE
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/gitea-mirror.sh)"
See the Proxmox VE Community Scripts for more details.
Zero-configuration deployment with Nix:
# Run immediately - no setup needed!
nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror
# Or build and run locally
nix build --extra-experimental-features 'nix-command flakes'
./result/bin/gitea-mirror
# Or install to profile
nix profile install --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror
gitea-mirror
NixOS users - add to your configuration:
{
inputs.gitea-mirror.url = "github:RayLabsHQ/gitea-mirror";
services.gitea-mirror = {
enable = true;
betterAuthUrl = "https://mirror.example.com";
openFirewall = true;
};
}
Secrets auto-generate, database auto-initializes. See NIX.md for quick reference or docs/NIX_DEPLOYMENT.md for full documentation.
# Install Bun
curl -fsSL https://bun.sh/install | bash
# Setup and run
bun run setup
bun run dev
First Time Setup
Mirror Strategies
Customization
Mirror Git LFS objects along with your repositories:
LFS_START_SERVER = true)Transfer complete repository metadata from GitHub to Gitea/Forgejo:
Enable in Settings β Mirror Options β Mirror metadata
Gitea Mirror provides powerful automatic synchronization features:
SCHEDULE_ENABLED=true or GITEA_MIRROR_INTERVAL is set - no manual clicks required!Navigate to the Configuration page and enable "Automatic Syncing" with your preferred interval.
π Set it and forget it! With these environment variables, Gitea Mirror will automatically:
# Option 1: Enable automatic scheduling (triggers auto-start)
SCHEDULE_ENABLED=true
SCHEDULE_INTERVAL=3600 # Check every hour (or use cron: "0 * * * *")
# Option 2: Set mirror interval (also triggers auto-start)
GITEA_MIRROR_INTERVAL=8h # Every 8 hours
# Other examples: 5m, 30m, 1h, 24h, 1d, 7d
# Advanced: Use cron expressions for specific times
SCHEDULE_INTERVAL="0 2 * * *" # Daily at 2 AM (optimize bandwidth usage)
# Auto-import new repositories (default: true)
AUTO_IMPORT_REPOS=true
# Auto-cleanup orphaned repositories
CLEANUP_DELETE_IF_NOT_IN_GITHUB=true
CLEANUP_ORPHANED_REPO_ACTION=archive # 'archive' (recommended) or 'delete'
CLEANUP_DRY_RUN=false # Set to true to test without changes
Important Notes:
SCHEDULE_ENABLED=true or GITEA_MIRROR_INTERVAL is set, the service automatically imports all GitHub repositories and mirrors them on startup. No manual "Import" or "Mirror" button clicks required!GITEA_MIRROR_INTERVAL determines how often each repository is actually synced. For example, with 8h, each repo syncs every 8 hours from its last successful sync.5m). Start with a longer interval (1h to 8h) or temporarily disable scheduling during the initial import/mirror run, then enable your regular interval after the first pass completes.π‘οΈ Backup Protection Features:
archive action preserves all repository data:
archived- prefix (Gitea/Forgejo API limitation prevents archiving mirrors)Manual Sync from the Repositories page whenever you need fresh data.CLEANUP_ORPHANED_REPO_ACTION=archive (default) instead of deleteGitea Mirror works with non-github.com GitHub deployments. Point the client at your Enterprise API via the GH_API_URL environment variable:
# GitHub Enterprise Server (self-hosted)
GH_API_URL=https://ghe.example.com/api/v3
# GitHub Enterprise Cloud with data residency
GH_API_URL=https://api.TENANT.ghe.com
Standard GitHub Enterprise Cloud on github.com needs no override. Use a token issued by the target Enterprise instance for GITHUB_TOKEN.
If you run behind a reverse proxy on a subpath (for example https://git.example.com/mirror), configure:
# BASE_URL handles the path prefix β auth URLs stay as origin only
BASE_URL=/mirror
BETTER_AUTH_URL=https://git.example.com
PUBLIC_BETTER_AUTH_URL=https://git.example.com
BETTER_AUTH_TRUSTED_ORIGINS=https://git.example.com
# β Auth endpoints resolve to: https://git.example.com/mirror/api/auth/*
Notes:
BASE_URL sets the application path prefix.BETTER_AUTH_URL and PUBLIC_BETTER_AUTH_URL should be origin only (e.g. https://git.example.com). Do not include the base path β it is applied automatically from BASE_URL. Any path accidentally included is stripped.BETTER_AUTH_TRUSTED_ORIGINS should also contain origins only (no path).BASE_URL is runtime configuration, so prebuilt registry images can be reused across different subpaths.For existing pull-mirror repositories, changing the GitHub token in Gitea Mirror does not always update stored mirror credentials in Gitea/Forgejo for already-created repositories.
If sync logs show authentication failures (for example terminal prompts disabled), do one of the following:
If you enable metadata options (issues/PRs/labels/milestones/releases) after repositories were already mirrored:
sqlite3 data/gitea-mirror.db "UPDATE repositories SET metadata = NULL;"
This clears per-repository metadata completion flags so the next sync can re-run metadata import steps.
MIN_INTERVALGitea Mirror treats the interval configured in Configuration (or GITEA_MIRROR_INTERVAL) as the source of truth and applies it to mirrored repositories during sync.
If your Gitea/Forgejo server has mirror.MIN_INTERVAL set to a higher value (for example 24h) and Gitea Mirror is set lower (for example 8h), sync/mirror operations can fail when updating mirror settings.
To avoid this:
MIN_INTERVAL.# Install dependencies
bun install
# Run development server
bun run dev
# Run tests
bun test
# Build for production
bun run build
ENCRYPTION_SECRET environment variable for production deploymentsBETTER_AUTH_SECRET if not setIf email delivery is not configured, an admin with server access can reset a user password from the command line:
bun run reset-password -- --email=user@example.com --new-password='new-secure-password'
What this does:
Use this only from trusted server/admin environments.
Gitea Mirror supports multiple authentication methods. Email/password authentication is the default and always enabled.
The standard authentication method. First user to sign up becomes the admin.
Enable users to sign in with external identity providers like Google, Azure AD, Okta, Authentik, or any OIDC-compliant service.
Configuration:
https://accounts.google.com)Redirect URL for your provider:
https://your-domain.com/api/auth/sso/callback/{provider-id}
Need help? The SSO & OIDC guide now includes a working Authentik walkthrough plus troubleshooting tips. If you upgraded from a version earlier than v3.8.10 and see TypeError β¦ url.startsWith after the callback, delete the old provider and add it again using the Discover button (see #73 and #122).
Perfect for automatic authentication when using reverse proxies like Authentik, Authelia, or Traefik Forward Auth.
Environment Variables:
# Enable header authentication
HEADER_AUTH_ENABLED=true
# Header names (customize based on your proxy)
HEADER_AUTH_USER_HEADER=X-Authentik-Username
HEADER_AUTH_EMAIL_HEADER=X-Authentik-Email
HEADER_AUTH_NAME_HEADER=X-Authentik-Name
# Auto-provision new users
HEADER_AUTH_AUTO_PROVISION=true
# Restrict to specific email domains (optional)
HEADER_AUTH_ALLOWED_DOMAINS=example.com,company.org
How it works:
Example Authentik Configuration:
# In your reverse proxy configuration
proxy_set_header X-Authentik-Username $authentik_username;
proxy_set_header X-Authentik-Email $authentik_email;
proxy_set_header X-Authentik-Name $authentik_name;
Gitea Mirror can also act as an OIDC provider for other applications. Register OAuth applications in Settings β Authentication & SSO β OAuth Applications tab.
Use cases:
Pull requests cannot be created as actual PRs in Gitea/Forgejo due to API limitations. Instead, they are mirrored as enriched issues with comprehensive metadata.
Why real PR mirroring isn't possible:
How we handle Pull Requests: PRs are mirrored as issues with rich metadata including:
This approach preserves all important PR information while working within Gitea/Forgejo's API constraints. The PRs appear in the issue tracker with clear visual distinction and comprehensive details.
Contributions are welcome! Please read our Contributing Guidelines for details on our code of conduct and the process for submitting pull requests.
GNU Affero General Public License v3.0 (AGPL-3.0) - see LICENSE file for details.
Hacker News (1)
TypeScript
78.9%
Nix
8.6%
MDX
7.4%
Astro
2.5%
Shell
1.9%