8 min readVPS
Our code lives on a Forgejo server we run ourselves. Five developers work on it, the repositories add up to about 5 GB, and it runs on the same KVM VPS 8 we sell to customers. This website deploys from it: a push to the main branch runs the tests, builds the site and ships it through Forgejo Actions. Nobody outside the company can push to it. Our documentation site deploys from it too, onto a cPanel account, and we wrote that pipeline up step by step in how to deploy a static site to cPanel on every push.
There are plenty of guides on installing Forgejo, and the official documentation is good, so this is not another one. It is about the part that comes after the install: what a small team's instance actually needs from a server, what an upgrade involves every quarter, what a backup has to contain to be worth anything, and the trade-off we made by running the CI runner on the same machine.
Why we host our own git server
The short version is that we want to own our code and to know exactly who has access to it. A hosted forge is convenient, and for many teams it is the right choice. But the list of people and systems that can read a private repository on someone else's platform is theirs to define, and it can change with a terms update we never agreed to. On our own server the list is the five of us.
The choice of Forgejo over Gitea, which it forked from, comes down to governance. In October 2022 the Gitea domains and trademark were transferred to a for-profit company without the community's approval, and Forgejo was created in response. In early 2024 it became a hard fork and the two codebases began to diverge. Forgejo is developed under Codeberg e.V., a non-profit, and publishes exclusively under Free Software licenses. For a tool whose whole point is that we control it, that matters.
What it runs on
Forgejo does not publish hardware requirements, so what follows is our experience rather than a specification.
The instance runs on a KVM VPS 8: 4 dedicated CPU cores, 8 GB of DDR5 ECC memory and 256 GB of Gen 4 NVMe storage. With five developers, about 5 GB of repositories and the CI runner on the same machine, it is nowhere near a limit, and we have not needed to size up. Git operations are bursty: a clone or a push wants CPU and disk for a few seconds and then nothing, and on this platform the cores are allocated to the server rather than carved out of a shared pool, so those seconds are not spent queueing.
What grows is not the repositories. According to the backup documentation, a complete Forgejo backup includes the database, the repositories, logs, the custom directory, LFS data, attachments, package data, the search index and repository archives. Every one of those is something that accumulates on disk. Source code is the smallest of them: 5 GB of repositories is two percent of this server's disk. A team that stores build outputs in the package registry or large binaries in LFS will fill the disk long before its source code does, so if anything ever needs watching it is disk, not CPU, and for a team our size that day is a long way off.
What an upgrade involves
This is the part that decides whether self-hosting is for you, because it recurs.
Forgejo publishes a stable release every quarter. A regular release receives full support, bug fixes and security fixes for three months, plus a two-week window after the next stable release appears. Long-term support releases, the version published in the first quarter of each year, receive critical bug fixes and security support for one year and three months. As of September 2026 the regular line is 16, and the LTS line is 15, supported until July 15, 2027. The release page carries the exact dates.
We run 16. That means an upgrade every three months or so, and we treat it as a small project rather than a click. The upgrade guide is short and worth following to the letter:
- Take a full backup. The guide calls it a requirement when moving to a new stable release, and the reason is the next point.
- Read the release notes for the version you are moving to. Since 7.0.0 Forgejo follows semantic versioning, so a breaking change only arrives when the first number changes, but a major upgrade can still call for manual steps, and the notes are where they are listed.
- Run
forgejo manager flush-queuesbefore stopping the old version. Queues hold serialized data that is not guaranteed to be compatible with the next release. - Replace the binary or the container image and start it. The database stores its schema version, so an accidental downgrade is refused rather than silently corrupting anything.
- Run
forgejo doctor check --alland make sure it reports nothing.
One upgrade this year asked for extra manual steps in its release notes, and we did them. So far nothing has broken, which we put down to the backup-first habit and reading the notes rather than to luck. If you would rather upgrade once a year, the LTS line is the better fit; you give up new features for a longer window.
The runner on the same machine
Forgejo Actions runs workflows through a separate program, the runner, which fetches jobs from the instance and executes them. The documentation is blunt about what that means: the runner performs remote code execution, and that "poses significant security threats for the host and network that it operates upon".
Ours runs on the same VPS as Forgejo. Whether that is reasonable depends on two things, and the security guide spells both out.
The first is how jobs execute. In Docker mode, the default, each job runs in a container and gets its own isolated network. In host mode, steps run as the runner's own user on the host with no isolation at all. Ours is Docker mode. Even so, the guide notes that executed commands use the host's network stack, which can expose other services on the same machine, so isolation is a matter of degree rather than a wall.
The second is who can trigger a workflow. A runner registered with a repository only runs that repository's actions, and a newcomer's first pull request needs a contributor's approval before any workflow executes. But anyone who can push directly to a repository can run arbitrary workflows on the runner, and no branch protection changes that. On our instance the people who can push are the five of us. The runner therefore trusts exactly the people who already have full access to the code, and putting it on a second machine would add a bill without changing who it trusts.
That reasoning stops the moment outside contributors can push, or the instance hosts code from more than one organization. Then the runner belongs on its own host, with whatever the jobs produce kept on its disk rather than next to the database. Read the security guide before registering a runner, not after.
Backups that restore
A backup is a claim until you have restored from it. Forgejo's forgejo dump command produces a
single archive with everything listed above, which is the simplest way to get a complete copy.
Ours goes to a facility in a different city, because a backup in the same building as the server
shares its fate in a fire, a flood or an operator's mistake.
Two things are easy to get wrong. The first is scope: a backup that skips LFS data, attachments or packages restores a repository with holes in it, so check the archive contains what the dump command says it does. The second is the runner: it is installed and configured separately from Forgejo, so its configuration is not in the dump, and a restore onto a fresh server means setting it up and registering it again. Write that down before you need it.
On our VPS plans, off-site backup storage is an add-on rather than something every server gets, so if you self-host a forge with us, add it.
What it costs, and when it is the wrong choice
For a five-person team the whole bill is the server: a self-managed KVM VPS 8 is $39.95 a month as of September 2026, and the software is free. Against per-seat pricing that is cheap. Against the hours, it is only cheap if someone on the team is happy to own a quarterly upgrade, the backup habit and the runner's security posture. We are, because it is the same work we do for customers all day.
If that someone does not exist, there are two alternatives. A hosted forge, where the trade is control for convenience. Or the same server as a fully managed VPS, where we harden the operating system before it enters service and keep it patched afterwards, and the application layer is a conversation to have before you order rather than an assumption. The questions in managed or self-managed settle it faster than any feature list.
If you are weighing this up for your own team, ask us. We run one, so the answer will be specific, including when it is "keep using the hosted one".