Category
A self-hosted forge is a great git remote and a poor GitHub replacement
Moving your repositories off GitHub is a two hour job. Replacing what GitHub gives you socially is not a job you can do at all, which is why the right answer is usually both.
What you can move, and what you cannot#
Repositories move easily. Issues, pull requests, releases and wikis move with a decent importer. What does not move is the network: the person who found your project through a GitHub search, filed a bug, and sent a patch because they already had an account. A self-hosted forge is an excellent git remote, a good private issue tracker, and a poor replacement for being visible where developers already are.
So do not frame it as a migration. Frame it as a primary and a mirror. Your forge is authoritative, your git push goes there, and a push mirror keeps a read-only copy on GitHub or Codeberg for discovery and for the day your server does not come back. Both Forgejo and Gitea do push mirroring in core. GitLab's pull mirroring is a Premium feature, which surprises people.
The second uncomfortable fact: if your forge runs on a machine in the same house as your laptop, you do not have an off-site copy of your code. A fire or a theft takes both. A git clone on the laptop is not the backup either, because it carries no issues, no releases, no LFS objects and no wiki.
The default#
Run Forgejo: one container, one volume, SQLite unless you choose otherwise, roughly 150 MB idle, serving a dozen developers on a 2 GB VPS without complaint. The reason is governance, not features. Forgejo belongs to Codeberg e.V. and has been GPL-3.0-or-later since v9.0 on 22 August 2024, so an open core split cannot be applied to it later. The cost is real: fewer eyes on the code, corporate legal reviews that flinch at GPL-3.0, and an Actions implementation that is thin next to GitHub's.
Do not take that advice in either of these cases. Your legal policy is permissive-only, or you are already on Gitea v1.23 or newer, where there is no supported direct migration and moving means exporting repository by repository. Stay on Gitea; it is the same software with a different owner. You want repositories, CI, a container registry, packages and environments in one upgrade unit and have the hardware for it: GitLab CE, with 8 GB minimum and 16 GB baseline per its own requirements page. Add Woodpecker CI when your forge's built-in CI runs out, Harbor only when you need scanning and RBAC over images, and code-server if the code should live on the server rather than the laptop.
The mistakes#
- Publishing port 3000 before finishing the installer. The first-run setup page is unauthenticated and the first account created becomes the instance administrator. Bind to localhost first, or pre-write
app.iniwithINSTALL_LOCK = true. - Mapping host 222 to container 22 and forgetting
SSH_PORT = 222. The clone URL comes fromapp.ini, not from Docker, so every developer copies a URL that times out.ROOT_URLbehind a proxy has the same failure shape for webhooks and OAuth redirects. - Restoring a dump into a different path. Every repository's git hooks contain the absolute path of the forge binary. Pushes then fail with an opaque hook error while the web UI looks healthy. Run the admin hook regeneration command after any move or restore.
- Backing up a running forge. The docs are explicit: shut down before
gitea dump, or the database and the repository files diverge mid-copy and you find out at restore time. - Trying SQLite in production and converting later. The documentation warns that engine conversion is not well tested. Pick PostgreSQL up front if you expect more than a handful of active users.
- Restoring GitLab without
/etc/gitlab.gitlab-backup createdeliberately excludesgitlab-secrets.json, so a restore comes up with every encrypted column unreadable: CI variables, integration tokens and two-factor seeds. - Assuming deleted Harbor tags free disk. They do not until garbage collection runs, and Harbor goes read-only while it does.
Read next#
Forgejo vs Gitea has the governance argument in full, Gitea vs GitLab CE the size one. Replace GitHub covers the mirror-and-migrate shape end to end. Then Backups that actually restore for the dump, Moving a service to a new machine for the hook-path trap, and A security baseline for a home server before you hand any runner a Docker socket.
Every developer tool we profile#
Sorted by operational load, lightest commitment first. Every row links to a full profile with the resource numbers, the upgrade traps and the restore path.
| Tool | Ops load | Idle RAM | Licence | Identity | Backup shape |
|---|---|---|---|---|---|
| Forgejo | 2, Light | 150 MB | GPL-3.0-or-later | Native OIDC | SQLite backup |
| Gitea | 2, Light | 150 MB | MIT | Native OIDC | SQLite backup |
| Woodpecker CI | 2, Light | 90 MB | Apache-2.0 | Native OIDC | SQLite backup |
| code-server | 2, Light | 200 MB | MIT | Auth proxy only | File copy |
| GitLab CE | 4, Heavy | 4096 MB | MIT | Native OIDC | Postgres dump |
| Harbor | 4, Heavy | 2500 MB | Apache-2.0 | Native OIDC | Postgres dump |
Profiles#
Forgejo
Nonprofit-governed Gitea fork, copyleft since v9.0, run by Codeberg e.V.
ops 2/5 · 150 MBGitea
MIT-licensed Git forge in a single Go binary, backed by a for-profit company
ops 2/5 · 90 MBWoodpecker CI
Apache-2.0 fork of Drone 0.8, kept free after Drone went proprietary
ops 2/5 · 200 MBcode-server
VS Code in a browser tab, on your server, without a Microsoft account
ops 4/5 · 4096 MBGitLab CE
The full GitLab platform, MIT-licensed FOSS build, with a genuinely large RAM floor
ops 4/5 · 2500 MBHarbor
CNCF container registry with RBAC, Trivy scanning, replication and retention policies
Head to head in this category#
- Forgejo vs GiteaNearly identical software, completely different ownership. Start today and the answer is Forgejo.
- Gitea vs GitLab CEOne is a git remote you forget about. The other is a DevOps platform with a documented 8 GB floor.
Leaving a cloud service in this category#
- Replace GitHubA self-hosted forge replaces GitHub's git hosting completely and its network not at all.
Questions#
Forgejo or Gitea?
Forgejo unless you have a specific reason not to. The feature sets are close enough that the decision is about control: Gitea's domains and trademark sit with Gitea Ltd, contributors sign a copyright assignment, and CommitGo sells a proprietary Gitea Enterprise on the MIT core. Forgejo belongs to Codeberg e.V., a German nonprofit, and has been GPL-3.0-or-later since v9.0 on 22 August 2024, which makes that pattern structurally impossible. The exception is timing: Forgejo v10.0 was the last release that reads a Gitea v1.22 or older database, so from Gitea v1.23 onward there is no supported direct migration.
Is my self-hosted forge a backup of my code?
Not if it lives in the same building as the machine you code on. A fire, a theft or a failed PSU takes both copies at once, and git clone on a laptop is not a backup either, because it does not carry issues, pull requests, releases, LFS objects or the wiki. Use the forge's push mirroring to a second remote you do not own, and separately run forgejo dump or gitea dump on a schedule with the service stopped. Restore that dump once, into a scratch host, before you trust it.
How much RAM does GitLab CE actually need?
GitLab's requirements page states 8 GB as the minimum for memory-constrained environments and 16 GB with 8 vCPU as the baseline, and separately advises disabling swap because it degrades performance badly under load. That combination means a 4 GB instance has no safety valve: Puma and Sidekiq workers get OOM-killed under CI load and jobs stall silently. For comparison, Forgejo or Gitea serve a dozen developers from about 150 MB. Run GitLab when you want repos, CI, a registry, packages and environments in one upgrade unit.
Do I need Harbor, or is registry:2 enough?
registry:2 is one container and about 30 MB. Harbor is roughly ten containers, a bundled PostgreSQL and Redis, a documented 4 GB minimum with 8 GB recommended, and 40 GB of disk before you push anything. Pay that only for things registry:2 genuinely lacks: per-project RBAC, OIDC login, Trivy scanning with policies that block vulnerable pulls, proxy cache projects that mirror upstream past Docker Hub rate limits, and replication. Note that released Harbor image tags are still amd64 only, so an arm64 host is a real constraint.
Can I run CI on the same machine as my forge?
You can, and you should not. Both Gitea Actions' act_runner and Woodpecker's agent are normally given the host Docker socket so they can start job containers, and that is root-equivalent access to the host for anyone who can push a workflow file to a watched repository. On a forge that holds every repository you own, that is the whole prize. Run agents on a separate machine that holds nothing else, or use a rootless or Kubernetes backend. Woodpecker itself is small enough that a second box is cheap: server plus agent idle under 100 MB.
Does code-server give me the normal VS Code extensions?
No, and this decides whether it is usable for you. Microsoft's marketplace terms restrict it to Microsoft's own products, so code-server ships pointing at Open VSX. That covers ESLint, Prettier, the community Python and Go extensions, Vim keybindings and the popular themes. It cannot carry Microsoft's proprietary ones: Pylance, the C# Dev Kit, Live Share and the Remote SSH, Containers and WSL extensions are all absent. Check the extensions you depend on against open-vsx.org before you commit.
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.