April 24, 2026: GitHub begins training AI models on Copilot interaction data today — opted in by default for individual users, contractually excluded for enterprise customers. Two-tier privacy. Context ↓
Deployment Cookbook · Replit Users

The Sovereign Replit
Deployment Stack

This is the full formula — not just code backup, but a complete self-hosted CI/CD pipeline with a CDN edge layer. Built by WellSpr.ing after GitHub suspended 32 civic AI repositories without warning on April 18, 2026. Documented here as a gift. Freely given, so freely given.

"GitHub is a discovery layer, not a git host. Your build pipeline should run on infrastructure you actually control."
— Lesson from the WellSpr.ing migration, April 2026

The four-layer stack

Each layer is independent. Add them in order — each one makes you meaningfully harder to disrupt.

# Layer 1 — Git (sovereignty)
git.yourname.org   Forgejo on Railway  ~$5/mo
  └─mirror→ codeberg.org       (nonprofit backup)
  └─mirror→ github.com         (discovery — optional)

# Layer 2 — CI builds (speed)
Hetzner cx23       2 vCPU / 4GB       €5/mo
  Forgejo act_runner → Go builds in ~45s, Node in ~3min
  vs. Replit shared CPU → 15-minute deploys

# Layer 3 — Edge (resilience)
Fly.io proxy       2 regions          ~free tier
  → points to Replit deployment URL
  → Bunny DNS health-checks both; fails over in <30s

# Layer 4 — CDN (performance + IP hygiene)
Bunny CDN          anycast edge        ~$1/mo
  Clean IP in front of Replit's shared egress
  Zone pull from Fly.io (not directly from Replit)

Total cost: ~$11/mo for a production-grade, platform-independent deployment. Replit stays as your development environment. You stop being single-homed on any single vendor.

"If your code only lives on GitHub, you don't actually own it. You're renting access to it."
— Anurag Vishwakarma, April 11 · 147k impressions

The Replit situation

Replit connects your project to GitHub automatically. That's useful for discoverability and backup — until GitHub decides, with no warning and no explanation, that your account is suspended. At that point your project's remote becomes unreachable. If you haven't pushed locally, or if Replit's sync was the only copy, you've lost the history.

The good news: Replit projects are almost always small. The median project fits comfortably under 10 MB. Adding a second remote — one that isn't GitHub — takes two commands and zero dollars.

Path A — Codeberg only (recommended for most users)

Codeberg is a nonprofit, community-governed Forgejo instance hosted in Berlin. Free for open source. No IP bans on record. Run by a German non-profit (Codeberg e.V.) under German law. Likely the best GitHub exit for the majority of Replit users.

1
Create a free Codeberg account and repo
Go to codeberg.org → sign up → New Repository. Match the name to your GitHub repo. Make it public or private — your call. Private repos are free on Codeberg.
2
Add Codeberg as a second remote in your Replit shell
Open the Replit shell tab and run:
git remote add codeberg https://codeberg.org/YOURUSERNAME/YOURREPO.git
git push codeberg --all
That's it. Your full history is now on Codeberg. You are no longer single-homed.
3
Keep it current — push to both on every significant commit
Replit's built-in push button goes to GitHub. For Codeberg, use the shell:
git push origin && git push codeberg
Or add a shell alias so it's one command:
# Add to ~/.bashrc in your Replit
alias gpa='git push origin && git push codeberg'
Tip: Codeberg supports SSH keys too. If you add your Replit SSH public key to your Codeberg account settings, you can push over SSH without entering a password.

Path B — Self-hosted Forgejo as primary (power users)

If you're running an AI agent pipeline, a civic project, or anything that makes dozens of API calls to GitHub per day, you want Forgejo as your primary — not just a mirror. This is the full three-mirror architecture documented at notgit.org/formula.

# Your sovereign primary — no rate limits, no ban risk, no TOS scrutiny
git.yourname.org (Forgejo on Railway, Hetzner, Fly.io)
  └─push-mirror→ codeberg.org/you/repo        (community discovery, nonprofit)
  └─push-mirror→ github.com/you/repo          (commercial discovery — optional)

The Railway Forgejo template gets you a running instance in about ten minutes. Full walkthrough: notgit.org/push-mirror-setup. Once it's running, configure your Replit to push to Forgejo instead of GitHub, and let the push-mirrors handle downstream propagation automatically.

Railway Forgejo cookbook — what actually works

Forgejo on Railway is the fastest path to a self-hosted primary. Three things that will save you hours if you know them going in:

1
Mount the volume at /data/git, not /data
Forgejo's Docker entrypoint script chowns /data/git and creates the git user there. If you mount a Railway volume at the root /data, the entrypoint sees an empty, root-owned directory and Forgejo fails to start. Mount at /data/git — the entrypoint handles the rest.
# Railway volume: Mount Path
/data/git      ✓ correct — entrypoint chowns this
/data          ✗ breaks entrypoint (root-owned, empty)
2
Set FORGEJO__repository__ROOT to the git subpath
Forgejo's default repository root is /data/repositories. But the entrypoint only creates and chowns /data/git/repositories. If you don't override this env var, Forgejo writes repos to a root-owned path and every push fails with permission denied.
FORGEJO__repository__ROOT=/data/git/repositories   
# NOT /data/repositories — that path is root-owned
3
Always pass auto_init: true when creating repos via API
A repo created via the Forgejo API without auto_init: true exists in the database but has no git objects on disk. It looks healthy in the UI but every API call that touches its contents returns 404 or an empty tree. Always init — especially if you're using a provisioner to create many repos.
# POST /api/v1/user/repos
{
  "name": "my-repo",
  "auto_init": true,       ← required
  "default_branch": "main"
}
If you already have ghost repos (DB entry, no git objects), delete and recreate them with auto_init: true. There is no in-place repair.
Result: With these three fixes applied, a Forgejo instance on Railway can provision hundreds of repositories reliably — auto-init, push-mirror to Codeberg, and serve the Forgejo API without errors.

Layer 2 — CI builds on a €5 Hetzner server

Replit's 15-minute deploy is a shared-CPU bottleneck on the containerized build. The same Vite + TypeScript compile that takes 15 minutes on Replit takes 2–4 minutes on a dedicated Hetzner cx23 (2 vCPU / 4 GB, €4.99/mo). Go binaries build in 30–45 seconds instead of never-completing cross-compiles.

The mechanism is Forgejo Actions — the self-hosted equivalent of GitHub Actions — running on a dedicated act_runner process on the Hetzner box.

1
Create the Hetzner server via API
# cx23: 2 vCPU / 4 GB RAM / €4.99/mo — best value for CI
curl -X POST https://api.hetzner.cloud/v1/servers \
  -H "Authorization: Bearer $HETZNER_CLOUD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "myproject-builder",
    "server_type": "cx23",
    "image": "ubuntu-24.04",
    "location": "nbg1",
    "ssh_keys": [YOUR_SSH_KEY_ID]
  }'
Hetzner exposes a full REST API. The Replit secrets vault stores HETZNER_CLOUD_TOKEN. This call returns a public IP in under 30 seconds.
2
Install Go, Node.js, Docker, and act_runner
Use cloud-init user_data in the server creation payload to run the setup script on first boot. Key installs:
# Go 1.22 (for cross-compiled binaries)
wget https://go.dev/dl/go1.22.3.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.22.3.linux-amd64.tar.gz

# Node.js 20 LTS (for Vite/npm builds)
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs

# Forgejo act_runner v0.4.1
curl -fsSL https://gitea.com/gitea/act_runner/releases/download/v0.4.1/act_runner-0.4.1-linux-amd64 \
  -o /usr/local/bin/act_runner && chmod +x /usr/local/bin/act_runner
Note: The Gitea act_runner download URL changed in 2024. The dl.gitea.com/act_runner/latest/ path returns 404. Use the direct Gitea release URL above.
3
Enable Forgejo Actions and register the runner
In your Forgejo admin panel (git.yourname.org/-/admin), expand Actions → Runners in the sidebar — the feature is already enabled in Forgejo 10.x. Click "Create Runner" to get a registration token, then on the Hetzner server:
# Run as the runner user on the Hetzner box
act_runner register \
  --no-interactive \
  --instance https://git.yourname.org \
  --token YOUR_REGISTRATION_TOKEN \
  --name myproject-hetzner \
  --labels ubuntu-latest:docker://node:20-bullseye-slim
Then create a systemd service so it survives reboots (see notgit.org/formula for the unit file). The runner appears in your admin panel within seconds and is available to all repos in the org.
4
Add a .forgejo/workflows/release.yml to your repo
Forgejo Actions uses the same YAML syntax as GitHub Actions. A minimal Go release workflow:
# .forgejo/workflows/release.yml
name: Release
on:
  push:
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Linux
        run: |
          GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
            go build -ldflags "-s -w" -o myapp-linux .
      - name: Create Release
        uses: actions/forgejo-release@v2
        with:
          direction: upload
          release-dir: .
          release-notes: "Binary release"
Push a v1.0.0 tag and the runner picks it up, builds cross-platform binaries, and attaches them to the Forgejo release — all on your €5 server, not Replit's shared pool.
Speed comparison: WellSpr.ing's ody-agent (a Go daemon): 30 seconds on Hetzner cx23 vs ~4 minutes estimated on Replit shared CPU. The full Node.js / Vite app: 2–3 minutes on dedicated CPU vs 15 minutes on Replit. The gap widens with project size.

Layer 3 — Edge resilience with Fly.io + Bunny DNS

Replit's deployment gives you a single *.replit.app endpoint on shared infrastructure with a shared IP pool. Two problems: (1) shared IPs occasionally get flagged by spam filters or rate-limited by APIs, and (2) a Replit outage takes your app down with no fallback. The edge layer solves both.

1
Create a Fly.io proxy app (free tier)
A one-file Node.js reverse proxy deployed on Fly.io. It forwards all traffic to your Replit deployment URL and adds your custom domain clean IP as the exit point:
# server.js — the entire Fly.io app
const http = require('http');
const { createProxyMiddleware } = require('http-proxy-middleware');
const TARGET = process.env.UPSTREAM_URL;
// e.g. https://myapp.yourusername.repl.co

http.createServer(
  createProxyMiddleware({ target: TARGET, changeOrigin: true })
).listen(8080);
fly launch from your Replit shell — it creates a Fly.io app with a dedicated IPv4 and routes traffic to Replit. Your custom domain (via CNAME to myapp.fly.dev) now has a clean IP address.
2
Put Bunny DNS in front with health checks
Bunny DNS (bunny.net) costs $1–2/month for CDN pull zones and gives you anycast DNS with sub-30-second failover. Point your domain's A record at the Fly.io IP. Configure a health check on both the Fly.io proxy and your Replit deployment URL. If Fly goes down, Bunny reroutes to Replit directly. If Replit goes down, you know in 30 seconds.
# Bunny DNS zone — your domain
A    @    FLY_APP_IP        TTL 60   # primary (clean IP)
A    @    REPLIT_IP         TTL 60   # fallback (health-check gated)
Why bother with Fly.io? Replit's shared egress IPs come from a pool shared by thousands of apps. Some mail servers and APIs rate-limit or block entire Replit IP ranges. A dedicated Fly.io IP is yours alone — meaningfully cleaner for transactional email deliverability and API integrations.

Full cost table

LayerServiceCostWhat it does
Git primaryRailway (Forgejo)$5/moSelf-hosted git host, API, push mirrors to Codeberg
CI runnerHetzner cx23€5/moForgejo act_runner — 45s Go builds, 3min Node builds
CDN / EdgeBunny CDN~$1/moAnycast IP, pull zone, DNS failover, DDoS absorption
ProxyFly.iofree tierClean dedicated IP, geo-proxy, Replit abstraction layer
Code mirrorCodebergfreeNonprofit backup, community discovery
Dev environmentReplitexistingAI-assisted development, hot reload, always-on URL
Total added~$11/moFull sovereign deployment stack

This entire cookbook was built in a 35-minute compute sprint by a Replit AI agent after GitHub suspended the WellSpr.ing account. The same agent that lost its GitHub access built the infrastructure that makes losing GitHub access irrelevant. The recipe is the proof.
— WellSpr.ing, April 2026
Transparency — NotGit.org's own stack

NotGit.org runs on exactly this architecture. The source lives at git.wellspr.ing/wellspring/notgit — self-hosted Forgejo on Railway, mirrored to Codeberg. Deployments are built by a Forgejo act_runner on Hetzner. The domain sits behind Bunny CDN with a Fly.io proxy as the primary endpoint. We wouldn't publish a cookbook we don't eat from.

Why Replit users are specifically at risk

FactorRisk
AI-assisted coding at speedReplit's AI agent can commit and push faster than a developer working alone. Rapid automated commits match GitHub's suspension trigger patterns.
GitHub as the only remoteReplit's sync button pushes to GitHub only. One GitHub suspension = complete loss of remote access.
"Sign in with GitHub" dependenciesIf your app uses GitHub OAuth, a ban cuts off your users too — not just your code.
No warning, no appeal SLAThe pattern documented at notgit.org/incident/2026-04-18 — suspension with no notice, appeal replies taking days or never arriving.

The five rules for AI-era Replit development

01
Never single-home your code
Add Codeberg as a remote before your first serious push. The fix costs twenty minutes. The alternative costs everything.
02
Don't let your AI agent push directly to GitHub
Have it push to your Forgejo primary or to a local branch. Let the push-mirror carry it forward. Rapid automated pushes to GitHub are a known trigger.
03
Scope your PATs minimally
If you give Replit's AI a GitHub token, give it repo scope only — not admin:org, not delete_repo. Broad tokens making many calls per minute match the detection pattern.
04
Keep a local clone somewhere other than GitHub
Replit's filesystem is ephemeral across plans. Codeberg, Forgejo, or even a local machine clone. Three copies: one working, one Codeberg, one home.
05
Don't use "Sign in with GitHub" for anything critical
If a ban suspends your GitHub account, GitHub OAuth fails too. Use Replit Auth, email/password, or a separate OAuth provider for apps that need to stay up.

"GitHub is still the best place to collaborate and be discoverable. Nothing wrong with using it. The problem is using it as the only copy."
— Anurag Vishwakarma, on the lesson his account suspension taught him

Resources