Git Sovereignty: Setting Up Gitea on Synology NAS, Mirroring GitHub, and Mastering Dual Remotes
# Git Sovereignty: Setting Up Gitea on Synology NAS, Mirroring GitHub, and Mastering Dual Remotes
## 正文
*中文版: [代码资产自托管:群晖 NAS 搭建 Gitea、GitHub 镜像备份与本地双 Remote 实战](https://www.icsteve.com/zh/1552.html)*
In modern software and hardware engineering, Git is the universal fabric of collaboration. Yet, many engineers conflate Git—the distributed version control system—with GitHub, the proprietary cloud platform owned by Microsoft.
![]()
When Linus Torvalds designed Git in 2005, his fundamental architectural principle was radical decentralization: every clone is a fully functional repository with complete historical provenance. There is no inherent "master server" in Git’s protocol. However, modern developer workflows have introduced a single point of failure by centralizing around third-party cloud hosts.
Relying exclusively on a single cloud remote introduces critical risks:
- Account lockouts and compliance triggers: Automated risk engines or DMCA takedowns can freeze critical repositories without warning.
- Upstream outages & network disconnects: When cloud DNS collapses or local ISPs face fiber cuts, internal development, CI/CD, and deployment pipelines should not grind to a halt.
- Intellectual Property & Privacy: Proprietary silicon automation scripts, EDA tooling, private financial ledgers, and personal journal notes belong behind your local firewall.
The solution is not abandoning GitHub—it remains the gold standard for developer visibility, open-source collaboration, and community discovery. The optimal engineering solution is Git Dual-Homing: using GitHub as your collaborative remote while hosting a private, low-latency, resilient Git instance on your local Synology NAS, complete with automated mirroring and dual-remote push capabilities.
Architectural Topology: The Multi-Remote Model
+-------------------------------------------+
| Cloud Remote: GitHub (Public/Team) |
+-------------------------------------------+
^ |
| | [Gitea Pull Mirror]
[git push origin] | v (Scheduled auto-sync)
+-----------------------------+
| Local Synology NAS: Gitea |
| (Local Intranet Hub: 3000) |
+-----------------------------+
^
| [git push gitea]
|
+--------------------------------------+
| Local Developer Workstation (Mac/PC) |
| - origin: github.com/user/repo |
| - gitea: nas-ip:2222/user/repo |
+--------------------------------------+
This topology provides:
- Zero-Latency LAN Operations: Full read/write performance over 2.5GbE/10GbE local networks.
- Automated Upstream Sync: Gitea pulls commits, branches, and tags from GitHub on a scheduled interval.
- Granular Branch Governance: Keep work-in-progress (WIP) and sensitive branches strictly on your NAS, pushing only clean, tagged releases to GitHub.
Part 1: Deploying Gitea on Synology NAS with Container Manager
Why Gitea?
Among self-hosted Git solutions (GitLab, Gogs, Forgejo, Gitea), Gitea is uniquely suited for NAS environments. Written in Go, it runs with exceptional resource efficiency (~80MB to 120MB of RAM) and near-zero idle CPU load, making it ideal for Synology’s x86 or ARM architecture.
![]()
Step 1: Directory Setup
Open File Station on your Synology DSM and navigate to your docker share. Create the following directory hierarchy:
/volume1/docker/gitea/
└── data/ <-- Repositories, SSH configurations, and SQLite database
Ensure the docker user group has read and write permissions to this directory.
Step 2: Container Manager docker-compose.yml
Open Container Manager in DSM:
- Navigate to Project -> Click Create.
- Name the project
giteaand set the path to/docker/gitea. - Select Create docker-compose.yml and paste the following configuration:
version: "3"
networks:
gitea_net:
driver: bridge
services:
server:
image: gitea/gitea:latest
container_name: gitea
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=sqlite3
- GITEA__database__PATH=/data/gitea/gitea.db
- GITEA__server__DOMAIN=192.168.1.100 # Your Synology LAN IP or local hostname
- GITEA__server__SSH_DOMAIN=192.168.1.100 # Your Synology LAN IP
- GITEA__server__SSH_PORT=2222 # Custom SSH port avoiding DSM port 22
- GITEA__server__SSH_LISTEN_PORT=22
- GITEA__server__ROOT_URL=http://192.168.1.100:3000/
- GITEA__server__LFS_START_SERVER=true
- TZ=America/Los_Angeles
networks:
- gitea_net
volumes:
- /volume1/docker/gitea/data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000" # Gitea Web UI
- "2222:22" # Git SSH forwarded from container port 22
Why Port 2222? Synology DSM reserves port
22for host administration. Mapping container port22to external port2222allows Git SSH operations without interfering with system management.
Complete the wizard and start the project. Gitea will initialize in seconds.
Step 3: Initial Setup Wizard
- Visit
http://<SYNOLOGY_IP>:3000in your browser. - Confirm the prefilled settings (SQLite3, SSH Port
2222, Base URLhttp://<SYNOLOGY_IP>:3000/). - Scroll down to Administrator Account Settings and configure your primary admin user.
- Click Install Gitea.
Part 2: Backing Up (Mirroring) GitHub to Local Gitea
Gitea includes a native Mirror Migration engine. Unlike basic backup scripts that take static snapshots, a mirror continuously synchronizes branches, tags, and commits.
Method A: Web UI Automated Pull Mirror (Recommended)
- In the Gitea header, click the
+icon and select New Migration. - Select GitHub.
- Fill out the migration form:
- Clone Address:
https://github.com/<username>/<repo>.git - Authentication: For private repositories, enter a GitHub Personal Access Token (PAT) with
reposcope. - Mirror Option (CRITICAL): Check the box: This repository will be a mirror.
- Sync Interval: Select
4h,8h, or24h.
- Clone Address:
- Click Migrate Repository.
Gitea will clone the repository and run automated background syncs. Your Synology NAS will maintain an exact historical replica of your upstream GitHub projects.
Method B: CLI Manual Mirror Sync
If you need an immediate local clone before configuring web mirrors:
# 1. Clone a bare mirror from GitHub
git clone --mirror https://github.com/username/my-project.git
# 2. Enter repository directory
cd my-project.git
# 3. Add local Gitea remote
git remote set-url --push origin ssh://git@192.168.1.100:2222/username/my-project.git
# 4. Push all refs and tags to Gitea
git push --mirror
Part 3: Setting Up a 2nd Git Remote on Your Local Workstation
You do not have to choose between GitHub and Gitea on your developer machine. Git natively supports multiple remotes.
Strategy 1: Separate Remotes for Granular Control (Recommended)
1. Inspect Existing Remotes
git remote -v
Output:
origin git@github.com:steve/my-project.git (fetch)
origin git@github.com:steve/my-project.git (push)
2. Add Gitea as a Second Remote
git remote add gitea ssh://git@192.168.1.100:2222/steve/my-project.git
Verify your remotes:
gitea ssh://git@192.168.1.100:2222/steve/my-project.git (fetch)
gitea ssh://git@192.168.1.100:2222/steve/my-project.git (push)
origin git@github.com:steve/my-project.git (fetch)
origin git@github.com:steve/my-project.git (push)
3. Push Selectively
- Push WIP or private branches only to your NAS:
git push gitea feature/timing-closure-opt - Push public releases to GitHub:
git push origin main - Full backup push to Gitea:
git push gitea --all && git push gitea --tags
Strategy 2: Dual-Push with a Single Command (origin)
If you want every push to sync to both GitHub and Synology Gitea simultaneously, you can register multiple push URLs under origin:
# 1. Ensure GitHub remains the primary push URL
git remote set-url --add --push origin git@github.com:steve/my-project.git
# 2. Add Gitea as a second push URL under origin
git remote set-url --add --push origin ssh://git@192.168.1.100:2222/steve/my-project.git
Verify the configuration:
origin git@github.com:steve/my-project.git (fetch)
origin git@github.com:steve/my-project.git (push)
origin ssh://git@192.168.1.100:2222/steve/my-project.git (push)
Now, running a single command:
git push origin main
Will sequentially push commits to both GitHub and your local Synology Gitea instance.
Part 4: SSH Client Configuration
To streamline SSH connections on port 2222, configure your local ~/.ssh/config:
Host synology-gitea
HostName 192.168.1.100
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519
This simplifies your remote command to:
git remote add gitea synology-gitea:steve/my-project.git
Remember to paste your public key (cat ~/.ssh/id_ed25519.pub) into Gitea under User Settings -> SSH / GPG Keys.
Part 5: Disaster Recovery — How to Back Up and Rebuild Gitea
A self-hosted service is only as resilient as its disaster recovery procedure. Containers and application binaries are inherently disposable; your Git repositories, SQLite/PostgreSQL database, user accounts, and configuration secrets (SECRET_KEY, INTERNAL_TOKEN) are your core assets.
Gitea provides an official, atomic backup utility: gitea dump. It packages the database dump, raw repositories, Git LFS data, custom configurations, and server keys into a single compressed .zip archive without stopping the service.
1. Creating an Atomic Backup
Scenario A: Synology Package Center (SynoCommunity native package)
If you installed Gitea via the Synology Package Center (SynoCommunity), Gitea runs under the dedicated system service user sc-gitea. Execute the backup command directly in the Synology SSH terminal:
sudo su -s /bin/sh -c "/var/packages/gitea/target/bin/gitea dump -c /var/packages/gitea/var/conf.ini" sc-gitea
Command Breakdown:
sudo su -s /bin/sh -c "..." sc-gitea: Runs the dump sub-command under thesc-giteaservice account to ensure all file read permissions are respected and the output archive maintains correct ownership./var/packages/gitea/target/bin/gitea dump: Invokes Gitea’s built-in CLI backup sub-command.-c /var/packages/gitea/var/conf.ini: Explicitly points to the active Synology package configuration file.
This command generates a timestamped archive (e.g., gitea-dump-1727600000.zip) in the current working directory.
Scenario B: Docker / Container Manager
If running Gitea inside Docker, execute gitea dump inside the container:
docker exec -u 1000 -it gitea /app/gitea/gitea dump -c /data/gitea/conf/app.ini --file /data/gitea-dump-backup.zip
The resulting gitea-dump-backup.zip will immediately be stored on your host at /volume1/docker/gitea/data/gitea-dump-backup.zip.
2. Restoring & Rebuilding Gitea from Scratch
If your NAS storage crashes or you are migrating to a brand-new Synology unit, follow this sequential rebuild protocol:
Step 1: Deploy a Fresh Gitea Instance
Spin up a fresh Gitea instance using Container Manager (using the docker-compose.yml provided in Part 1) or reinstall via Package Center. Once launched, immediately stop the service to avoid database locking during file restoration:
# For Docker:
docker stop gitea
# For Package Center:
sudo synopkg stop gitea
Step 2: Unzip the Dump Archive
Transfer your gitea-dump-*.zip to the server and extract it into a temporary staging folder:
unzip gitea-dump-*.zip -d /tmp/gitea-restore/
The archive contains the following essential structures:
repos/: All bare Git repositories.gitea-db.sql: The complete database dump.data/: Attachments, avatars, and Git LFS objects.app.ini: Master server configuration including encryption keys.
Step 3: Restore Data & Database
For a Docker-based deployment:
- Restore repositories:
cp -R /tmp/gitea-restore/repos/* /volume1/docker/gitea/data/git/repositories/ - Restore configuration:
cp /tmp/gitea-restore/app.ini /volume1/docker/gitea/data/gitea/conf/app.ini - Restore database (SQLite):
sqlite3 /volume1/docker/gitea/data/gitea/gitea.db < /tmp/gitea-restore/gitea-db.sql - Restore data & LFS:
cp -R /tmp/gitea-restore/data/* /volume1/docker/gitea/data/gitea/
Step 4: Fix Permissions
Incorrect UID/GID permissions will prevent Gitea from reading repositories over SSH or HTTP. Reset ownership to match your deployment:
# Docker environment (matching UID 1000 in compose file):
sudo chown -R 1000:1000 /volume1/docker/gitea/data
# Package Center environment:
sudo chown -R sc-gitea:sc-gitea /var/packages/gitea/var/
Step 5: Start Gitea & Resync Hooks
Start the container or package service:
docker start gitea
Log in as administrator, navigate to Site Administration -> Dashboard -> Operations, and run these two essential maintenance tasks:
- Resync pre-receive, update and post-receive hooks of all repositories.
- Resync missing SSH keys (rebuilds the
.ssh/authorized_keysfile).
Your Gitea instance is now 100% recovered with all history, users, and mirrors intact.
Part 5: Disaster Recovery — How to Back Up and Rebuild Gitea
Gitea provides an official atomic backup command gitea dump without taking the service offline.
1. Atomic Backup Command
Scenario A: Synology Package Center (SynoCommunity)
sudo su -s /bin/sh -c "/var/packages/gitea/target/bin/gitea dump -c /var/packages/gitea/var/conf.ini" sc-gitea
Scenario B: Docker / Container Manager
docker exec -u 1000 -it gitea /app/gitea/gitea dump -c /data/gitea/conf/app.ini --file /data/gitea-dump-backup.zip
2. 5-Step Rebuild Protocol
- Deploy clean instance & stop immediately (
docker stop gitea); - Extract archive (
unzip gitea-dump-*.zip -d /tmp/gitea-restore/); - Restore data & DB (copy repos, restore
app.ini, import SQL); - Fix ownership (
sudo chown -R 1000:1000 /volume1/docker/gitea/data); - Start & resync hooks in Admin dashboard.
Summary Comparison
| Dimension | Cloud Only (GitHub) | Dual-Homed (GitHub + Synology Gitea) |
|---|---|---|
| Availability | Dependent on external internet & SaaS health | 100% LAN uptime at 2.5G/10G network speeds |
| Data Sovereignty | Subject to third-party policy changes | Stored on local Btrfs RAID with snapshot capabilities |
| Backup Cadence | Manual exports / API rate limits | Automated pull mirror + simultaneous dual push |
| Branch Governance | Binary (entire repository is public or private) | Flexible: WIP on local Gitea, clean releases on GitHub |
Self-hosting Git on a local NAS alongside GitHub restores the original distributed design of Git. It secures your intellectual property, protects against upstream disruptions, and gives you total sovereignty over your engineering assets.