Deploy Gitea Mirror | (Just Updated) GitHub to Gitea and Forgejo Backup, Admin Preset, Data Survives Redeploys

Gitea Mirror: back up GitHub repos to Gitea or Forgejo, admin preset

Deploy Gitea Mirror | (Just Updated) GitHub to Gitea and Forgejo Backup, Admin Preset, Data Survives Redeploys

Just deployed

/app/data

Deploy and Host Gitea Mirror on Railway

Built for an LLM agent to deploy and use. The deploy form has no fields to fill, and the admin account is created for you at first boot, so nobody can claim the instance by opening the page first. Gitea Mirror is an open-source, self-hosted service that backs up GitHub repositories, organisations and starred repositories into your own Gitea or Forgejo server and keeps them in sync on a schedule.

About Hosting Gitea Mirror

This template runs one service, gitea-mirror: a Bun and Astro web app with its SQLite database and generated secrets on a volume mounted at /app/data. You connect a GitHub token and a Gitea or Forgejo token on its Configuration page (or through the API), pick what to mirror, and it keeps the copies current.

Admin preset. Upstream lets the first visitor register and become the only admin, so a fresh public deploy belongs to whoever finds the URL first, and an agent has no login to use. This template generates ADMIN_EMAIL and ADMIN_PASSWORD per deploy and creates that account at first boot; upstream then refuses every further sign-up. Redeploys keep the account, the database and the secrets because they live on the volume.

Using it. Sign in at your domain with the two variables, or use the API: sign in once, create an API key (gm_ prefix), and send it in the x-api-key header on every call. Keys can be read only or full access, and a key cannot mint more keys.

Agent contract

  • Public URL: https://RAILWAY_PUBLIC_DOMAIN (the generated domain of the gitea-mirror service; also in the variable BETTER_AUTH_URL)
  • Private URL: http://gitea-mirror.railway.internal:8080
  • Credentials: ADMIN_EMAIL and ADMIN_PASSWORD (generated; the API key is created from them)
  • Auth: sign in with POST /api/auth/sign-in/email (JSON body, an Origin header equal to BETTER_AUTH_URL), create a key with POST /api/auth/api-key/create using that session cookie, then send the key as the x-api-key header; a call without a key or with a wrong key answers 401
  • Health: GET /api/health returns 200
#!/bin/sh
# needs: BETTER_AUTH_URL, ADMIN_EMAIL, ADMIN_PASSWORD (variables of the gitea-mirror service); also curl
J="Content-Type: application/json"
curl -s -w "\nhealth: %{http_code}\n" "$BETTER_AUTH_URL/api/health" | tail -n 1
curl -s -o /dev/null -w "sign-in: %{http_code}\n" -c /tmp/gm.jar -X POST -H "$J" -H "Origin: $BETTER_AUTH_URL" -d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASSWORD\"}" "$BETTER_AUTH_URL/api/auth/sign-in/email"
KEY=$(curl -s -b /tmp/gm.jar -X POST -H "$J" -H "Origin: $BETTER_AUTH_URL" -d '{"name":"agent"}' "$BETTER_AUTH_URL/api/auth/api-key/create" | sed -n 's/.*"key":"\([^"]*\)".*/\1/p')
curl -s -o /dev/null -w "read with key: %{http_code}\n" -H "x-api-key: $KEY" "$BETTER_AUTH_URL/api/dashboard"
curl -s -o /dev/null -w "read without key: %{http_code}\n" "$BETTER_AUTH_URL/api/dashboard"
curl -s -o /dev/null -w "write without key: %{http_code}\n" -X POST -H "$J" -d '{"owner":"octocat","repo":"hello-world"}' "$BETTER_AUTH_URL/api/sync/repository"
curl -s -o /dev/null -w "write with wrong key: %{http_code}\n" -X POST -H "$J" -H "x-api-key: gm_wrong" -d '{"owner":"octocat","repo":"hello-world"}' "$BETTER_AUTH_URL/api/sync/repository"
curl -s -o /dev/null -w "sign-up after admin: %{http_code}\n" -X POST -H "$J" -H "Origin: $BETTER_AUTH_URL" -d '{"email":"x@example.com","password":"password12345","name":"x"}' "$BETTER_AUTH_URL/api/auth/sign-up/email"

A fresh deploy prints health: 200, sign-in: 200, read with key: 200, read without key: 401, write without key: 401, write with wrong key: 401 and sign-up after admin: 403. To mirror a repository, add your GitHub and Gitea tokens on the Configuration page, then POST /api/sync/repository with the key.

Why Deploy Gitea Mirror on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Gitea Mirror on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

  • Agent-ready - no deployer fields, a contract block and one script that proves health, sign-in, API keys and the refusals.
  • Not claimable by a stranger - the admin exists before the first visitor, and sign-up is closed.
  • Data survives redeploys - database, admin and secrets sit on a volume at /app/data.
  • Pinned - the upstream image is pinned by digest.
  • Your own copy of your code - GitHub repositories, forks and stars land on a Gitea or Forgejo server you control.

Common Use Cases

  • Backing up GitHub - keep a current copy of every repository and organisation on your own server.
  • Leaving GitHub gradually - mirror first, switch remotes when ready.
  • Archiving starred repositories - keep projects that may be taken down.
  • Feeding a self-hosted CI - build from Gitea or Forgejo copies of public repositories.

Dependencies for Gitea Mirror Hosting

  • The gitea-mirror service with a volume, created by the template. A Gitea or Forgejo server to mirror into, and a GitHub token, are yours to supply.

Deployment Dependencies


Template Content

More templates in this category

View Template
RustFS | (Just Updated) S3 Object Store, Public URL and Keys Set
S3-compatible object store, keys and public URL preset, volume kept

SuperSlowSloth
0
View Template
TimescaleDB
A PostgreSQL-based time-series database for analytics on Railway.

Rakit
1
View Template
NEW
Neo4j 2026.10 Community
Neo4j graph database with APOC, Browser, Bolt TCP proxy and a volume

My Projects
0