feat(gitea-mirror): split gitea-mirror out of the gitea stack into its own service

This commit is contained in:
tiennm99 committed 2026-10-05 15:59:04 +07:00
1 parent 5caa497d7f
commit 0a7e8f9783
11 files changed
+97 -61

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
BETTER_AUTH_SECRET=
ENCRYPTION_SECRET=
GITEA_MIRROR_URL=https://gitea-mirror.example.com
GITEA_CLONE_TIMEOUT=3600
+3
View File
@@ -0,0 +1,3 @@
gitea-mirror/
.env
purge.py
+57
View File
@@ -0,0 +1,57 @@
# gitea-mirror
[gitea-mirror](https://github.com/RayLabsHQ/gitea-mirror) mirroring GitHub
repositories into a Gitea instance.
## Services
| Service | Image | Internal port | Domain |
| --- | --- | --- | --- |
| `gitea-mirror` | `ghcr.io/raylabshq/gitea-mirror:latest` | 4321 | `GITEA_MIRROR_URL` |
In Coolify, give `gitea-mirror` a domain on port 4321 matching
`GITEA_MIRROR_URL`. The image ships its own health check.
## Variables
| Variable | Feeds | Notes |
| --- | --- | --- |
| `BETTER_AUTH_SECRET` | gitea-mirror | Signs sessions and encrypts its login keys. Generate with `openssl rand -base64 32`. |
| `ENCRYPTION_SECRET` | gitea-mirror | Encrypts the stored GitHub and Gitea tokens. Generate with `openssl rand -base64 48`. |
| `GITEA_MIRROR_URL` | `BETTER_AUTH_URL`, `PUBLIC_BETTER_AUTH_URL`, `BETTER_AUTH_TRUSTED_ORIGINS` | Public URL of the mirror UI, no trailing slash. |
| `GITEA_CLONE_TIMEOUT` | `BUN_CONFIG_HTTP_IDLE_TIMEOUT` | Seconds to wait on Gitea's migrate API. Set it to the target Gitea's clone timeout. Defaults to `3600`; at most `14340`. |
Behind a reverse proxy, gitea-mirror rejects sign-in with "invalid origin"
unless all three Better Auth variables hold the external URL, so one variable
feeds them all.
Both secrets are set explicitly rather than left to the image, which would
otherwise generate its own into `gitea-mirror-data`. Data encrypted under one
secret is unreadable under another, so moving the data to a new deployment
means carrying the secrets with it. Never change either on an existing
install.
## Choices
- **Waits as long as Gitea clones.** Gitea's migrate API sends nothing until
the clone ends, and Bun's `fetch` drops a connection idle for 5 minutes.
gitea-mirror then marks the repository failed while Gitea keeps cloning; on
retry it finds the half-made repository and marks it mirrored. If that
clone later fails, Gitea keeps an empty repository with no mirror record,
which never syncs. `BUN_CONFIG_HTTP_IDLE_TIMEOUT` takes Gitea's clone
timeout so the request outlives the clone. Bun caps it at 239 minutes, so a
larger value stops helping there.
- **`gitea-mirror:latest`** with `pull_policy: always`: upstream publishes no
major tag, so every redeploy takes the newest release.
## Usage
Sign up at `GITEA_MIRROR_URL`, then in its settings enter the Gitea instance's
public URL and an access token, plus the GitHub token to mirror from. These
are stored in `gitea-mirror-data`, not in the environment.
## Storage
| Volume | Holds |
| --- | --- |
| `gitea-mirror-data` | Mirror job database, settings and generated secrets |
+17
View File
@@ -0,0 +1,17 @@
services:
gitea-mirror:
image: ghcr.io/raylabshq/gitea-mirror:latest
restart: unless-stopped
pull_policy: always
environment:
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?required}
ENCRYPTION_SECRET: ${ENCRYPTION_SECRET:?required}
BETTER_AUTH_URL: ${GITEA_MIRROR_URL:?required}
PUBLIC_BETTER_AUTH_URL: ${GITEA_MIRROR_URL:?required}
BETTER_AUTH_TRUSTED_ORIGINS: ${GITEA_MIRROR_URL:?required}
BUN_CONFIG_HTTP_IDLE_TIMEOUT: ${GITEA_CLONE_TIMEOUT:-3600}
volumes:
- gitea-mirror-data:/app/data
volumes:
gitea-mirror-data: