- Complete mirroring guide (GitHub ↔ Gitea) - GitHub Actions workflow to sync to Gitea - Mirror status check workflow - SSH and token authentication methods - Troubleshooting guide
11 KiB
Repository Mirroring: GitHub ↔ Gitea
Complete guide for setting up mirroring between GitHub and self-hosted Gitea.
Overview
Mirroring keeps two repositories in sync automatically. Choose your direction:
| Direction | Use Case |
|---|---|
| GitHub → Gitea | GitHub as primary, Gitea as backup/mirror |
| Gitea → GitHub | Gitea as primary, GitHub for visibility/CI |
| Bidirectional | Both accept commits (advanced) |
Method 1: Gitea as Mirror (GitHub → Gitea)
Gitea periodically pulls from GitHub. Best for backup scenarios.
Step 1: Create Mirror in Gitea
Via Web Interface:
- Go to https://git.terraphim.cloud
- Click "+" → "New Migration"
- Select "GitHub"
- Enter URL:
https://github.com/terraphim/gitea-infrastructure - Authentication:
- If private repo: Enter GitHub Personal Access Token
- If public: Leave empty
- Check "This repository will be a mirror"
- Click "Migrate Repository"
Via Gitea API:
# Get Gitea API token (Settings → Applications → Generate Token)
GITEA_TOKEN="your_gitea_api_token"
# Create mirror
curl -X POST \
"https://git.terraphim.cloud/api/v1/repos/migrate" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"clone_addr": "https://github.com/terraphim/gitea-infrastructure",
"repo_name": "gitea-infrastructure-mirror",
"repo_owner": "terraphim",
"mirror": true,
"auth_token": "your_github_token_if_private"
}'
Step 2: Configure Mirror Settings
- Go to repository Settings → Repository
- Scroll to Mirror Settings
- Set Synchronization Interval:
8h(or your preference) - Click "Update Mirror"
Step 3: Manual Sync (Optional)
# Trigger immediate sync
curl -X POST \
"https://git.terraphim.cloud/api/v1/repos/terraphim/gitea-infrastructure-mirror/mirror-sync" \
-H "Authorization: token ${GITEA_TOKEN}"
Method 2: GitHub as Mirror (Gitea → GitHub)
Push from Gitea to GitHub. Best when Gitea is your primary.
Step 1: Add GitHub Remote to Local Repository
cd /home/alex/infrastructure/gitea_config
# Add GitHub as mirror remote
git remote add github https://github.com/terraphim/gitea-infrastructure.git
# Or use SSH (recommended for automation)
git remote add github git@github.com:terraphim/gitea-infrastructure.git
Step 2: Configure Push Mirroring in Gitea
Option A: Gitea Push Mirror (Native)
- In Gitea, go to repository Settings → Repository
- Scroll to Mirror Settings
- Check "Push Mirror"
- Set Git Remote URL:
https://github.com/terraphim/gitea-infrastructure.git - Add authentication:
- Username: Your GitHub username
- Password/Token: GitHub Personal Access Token
- Set Interval:
8h - Click "Add Push Mirror"
Option B: Using Git Hooks (More Control)
Create a post-receive hook in Gitea:
- Go to repository Settings → Git Hooks
- Find post-receive hook
- Edit and add:
#!/bin/bash
# Push to GitHub after receiving commits
# Use deploy key or HTTPS with token
export GIT_SSH_COMMAND='ssh -i /path/to/deploy_key -o StrictHostKeyChecking=no'
git push github main --mirror
echo "Mirrored to GitHub"
Step 3: Test Push Mirroring
# Make a test commit
echo "# Test" >> README.md
git add README.md
git commit -m "test: Mirror sync"
# Push to Gitea
git push origin main
# Check GitHub (should appear within seconds)
Method 3: Bidirectional Mirroring
Both repositories accept commits. Warning: Can cause conflicts!
Architecture
Developer A → Gitea ←→ GitHub ← Developer B
↑___________↓
Bidirectional sync
Setup
-
Set up both mirrors:
- GitHub → Gitea (Method 1)
- Gitea → GitHub (Method 2)
-
Conflict Resolution Strategy:
- Always use
git mergeinstead ofgit push --force - Set up webhook for immediate sync
- Have clear contribution guidelines
- Always use
-
Webhook-based Immediate Sync:
In Gitea:
- Settings → Webhooks → Add Webhook → Gitea
- Target URL: Your sync service
- Events: Push
GitHub → Gitea sync service:
# sync-service.py (simplified) from flask import Flask, request import subprocess app = Flask(__name__) @app.route('/github-webhook', methods=['POST']) def github_hook(): # Trigger Gitea pull subprocess.run(['curl', '-X', 'POST', 'https://git.terraphim.cloud/api/v1/repos/terraphim/repo/mirror-sync']) return 'OK' @app.route('/gitea-webhook', methods=['POST']) def gitea_hook(): # Trigger GitHub push subprocess.run(['git', 'push', 'github', 'main']) return 'OK'
Method 4: Local Git Configuration (Simplest)
Push to both remotes simultaneously from your local machine.
Setup
cd /home/alex/infrastructure/gitea_config
# Check current remotes
git remote -v
# origin: should be Gitea
# Add GitHub as additional remote
git remote add github https://github.com/terraphim/gitea-infrastructure.git
# Or use SSH:
git remote add github git@github.com:terraphim/gitea-infrastructure.git
Push to Both
# Push to Gitea (origin)
git push origin main
# Push to GitHub
git push github main
Automate with Git Alias
Add to ~/.gitconfig:
[alias]
pushall = !git push origin $(git branch --show-current) && git push github $(git branch --show-current)
pa = pushall
Usage:
git pa # Pushes to both remotes
Automate with Bash Function
Add to ~/.bashrc or ~/.zshrc:
git-push-all() {
local current_branch=$(git branch --show-current)
echo "Pushing to all remotes..."
git push origin "$current_branch"
git push github "$current_branch"
echo "✅ Pushed to Gitea and GitHub"
}
alias gpa='git-push-all'
Authentication Setup
GitHub Personal Access Token
- Go to GitHub → Settings → Developer settings → Personal access tokens
- Generate new token (classic)
- Scopes needed:
repo(full control of private repositories)workflow(if using GitHub Actions)
- Copy token
Use Token in Gitea
When setting up push mirror:
- Username: Your GitHub username
- Password: Your Personal Access Token (not your password!)
SSH Key Setup (Recommended)
For automated pushes:
- Generate deploy key:
ssh-keygen -t ed25519 -f ~/.ssh/gitea_github_deploy -C "gitea-mirror"
-
Add public key to GitHub:
- Repository Settings → Deploy Keys → Add deploy key
- Paste contents of
~/.ssh/gitea_github_deploy.pub - Allow write access
-
Add private key to Gitea server:
# Copy to Gitea server
scp ~/.ssh/gitea_github_deploy bigbox:~/.ssh/
ssh bigbox "chmod 600 ~/.ssh/gitea_github_deploy"
- Configure SSH for GitHub in Gitea:
# Create SSH config
ssh bigbox "cat > ~/.ssh/config << 'EOF'
Host github-mirror
HostName github.com
User git
IdentityFile ~/.ssh/gitea_github_deploy
StrictHostKeyChecking no
EOF"
- Update remote to use SSH:
ssh bigbox "cd ~/gitea-stack && git remote set-url github git@github-mirror:terraphim/gitea-infrastructure.git"
Verification and Monitoring
Check Mirror Status
In Gitea:
- Repository → Settings → Repository → Mirror Settings
- Shows last sync time and status
Via API:
# Check mirror status
curl -s \
"https://git.terraphim.cloud/api/v1/repos/terraphim/gitea-infrastructure-mirror" \
-H "Authorization: token ${GITEA_TOKEN}" | \
jq '.mirror_updated, .mirror_interval'
Monitoring Script
Create check-mirror.sh:
#!/bin/bash
# Check if mirrors are in sync
echo "Checking repository sync status..."
# Get latest commit from GitHub
GITHUB_SHA=$(curl -s \
"https://api.github.com/repos/terraphim/gitea-infrastructure/commits/main" | \
jq -r '.sha')
# Get latest commit from Gitea
GITEA_SHA=$(curl -s \
"https://git.terraphim.cloud/api/v1/repos/terraphim/gitea-infrastructure/commits/main" | \
jq -r '.sha')
if [ "$GITHUB_SHA" == "$GITEA_SHA" ]; then
echo "✅ Repositories are in sync"
echo "Latest commit: ${GITHUB_SHA:0:7}"
else
echo "⚠️ Repositories are OUT OF SYNC"
echo "GitHub: ${GITHUB_SHA:0:7}"
echo "Gitea: ${GITEA_SHA:0:7}"
fi
Troubleshooting
Mirror Not Syncing
Check Gitea logs:
ssh bigbox "docker logs gitea | grep -i mirror"
Test manually:
ssh bigbox
cd ~/gitea-stack
git fetch github
git log HEAD..github/main # Check if behind
Authentication Failures
GitHub token expired:
- Generate new token in GitHub
- Update in Gitea mirror settings
SSH key issues:
ssh bigbox "ssh -T github-mirror"
# Should show: Hi terraphim/gitea-infrastructure! You've successfully authenticated
Conflict Resolution
If repositories diverge:
# On local machine
git fetch origin # Gitea
git fetch github # GitHub
# Check differences
git log --left-right --graph --cherry-pick --oneline origin/main...github/main
# Merge changes
git checkout main
git merge origin/main
git merge github/main
# Push to both
git push origin main
git push github main
Best Practices
1. One-Way vs Two-Way
- Use one-way for most cases (clear primary/secondary)
- Use two-way only for specific workflows with clear contribution guidelines
2. Sync Frequency
- Backup mirrors: 24h interval
- Active development: 1h interval or webhook-triggered
- CI/CD: Immediate webhook sync
3. Notifications
Set up alerts for sync failures:
- Gitea webhook on sync failure
- GitHub Actions monitoring
- Email notifications
4. Documentation
Document your mirroring strategy:
- Which repository is primary?
- Where should PRs be opened?
- How to handle conflicts?
Quick Reference
| Task | Command |
|---|---|
| Check remotes | git remote -v |
| Add GitHub remote | git remote add github https://github.com/terraphim/gitea-infrastructure.git |
| Push to all | git push origin main && git push github main |
| Force sync | curl -X POST "https://git.terraphim.cloud/api/v1/repos/terraphim/repo/mirror-sync" |
| Check status | git log origin/main..github/main |
Recommended Setup for Your Use Case
Primary: Gitea (self-hosted, private)
Mirror: GitHub (backup, CI/CD)
- Use Method 2 (Push Mirror) from Gitea to GitHub
- Set sync interval: 1 hour
- Use SSH authentication with deploy key
- Enable GitHub Actions for CI/CD on GitHub side
- Keep Gitea as single source of truth for commits
This gives you:
- ✅ Full control on Gitea
- ✅ GitHub as backup
- ✅ Can use GitHub Actions for CI/CD
- ✅ Simple one-way sync (less conflicts)