Which self-hosted Git server fit run for 1 GB VPS?
Compare bare repos over SSH, cgit, Forgejo, Gitea and GitLab by RAM. See the honest minimums and which self-hosted Git server fit your 1 GB VPS.
Wetin self-hosted Git server you suppose run
Self-hosted Git server no be one product, and RAM (random access memory) wey dey your VPS go decide which version you fit use. Git no need daemon of im own: bare repository plus SSH (secure shell) account don already be working server for the smallest box wey you fit rent. Everything wey pass this level na web application wey you choose to run beside am, and each higher level dey use memory wey small VPS fit no get.
Four levels dey. Bare repository over SSH, with nothing listening wey no dey listen before. cgit, na fast read-only web view wey no need database. Forgejo or Gitea, na full forge with accounts, issues and pull requests for a few hundred megabytes. GitLab, wey expect server wey big pass the others many times.
First decide the work wey you need do, then compare the memory figure with the plan wey you dey pay for.
Wetin RAM each option really need
Only two of these projects publish hardware figure. Treat published figure as minimum, no be promise, and measure your own instance after e don run, with systemd-cgtop or ps -o rss= -C forgejo.
The data behind this chart
[
{
"label": "Gitea, small team",
"ram_gb": 1
},
{
"label": "GitLab, memory constrained",
"ram_gb": 8
},
{
"label": "GitLab, single node baseline",
"ram_gb": 16
}
]Gitea document 1 GB RAM with 2 CPU cores as normally enough for small teams and projects, and e mention say Raspberry Pi 3 fit handle small workloads. GitLab document 16 GB as the baseline for one-node installation, and 8 GB as the low end for wetin its own page call memory constrained environment. Forgejo no publish any hardware requirement. Na fork of Gitea e be, and e behave like am, so the Gitea figure na the closest published guide wey you get.
Wetin this mean for 1 GB VPS: bare repositories and cgit fit run with extra RAM, because neither of dem run resident service. Forgejo or Gitea go start and serve small team with SQLite, but you dey on the documented minimum, so leave PostgreSQL and the CI (continuous integration) runner off that box. If the web interface disappear without error, run sudo dmesg -T | grep -i oom and look for line like Out of memory: Killed process 1181 (forgejo), wey mean say the kernel out of memory killer terminate am. GitLab for 1 GB box no be tuning problem. E no go run.
Tier 0: bare repository over SSH
Git no get any network daemon wey you need start. git push over SSH dey run git-receive-pack for the other side as normal Unix process, so any account wey you fit reach with key don already be Git remote. Create one account for the repositories, and keep the repositories outside the account home directory, because for Ubuntu 24.04, new home directory dey use mode 0750 and web view wey you add later no fit read inside am.
sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' \
--group --disabled-password --home /home/git git
sudo install -d -m 0755 -o git -g git /srv/git
sudo -u git git init --bare /srv/git/project.git--bare dey create repository wey no get working copy, and na this kind repository server dey hold. If you push enter repository wey get working copy, e go reject am with refusing to update checked out branch: refs/heads/main. Na the most common mistake for this tier.
Now give the account one key and clone am.
sudo -u git install -d -m 700 /home/git/.ssh
sudo -u git tee -a /home/git/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptop
EOF
sudo -u git chmod 600 /home/git/.ssh/authorized_keysgit remote add origin git@vps.example.com:/srv/git/project.git
git push -u origin mainFirst push wey work dey end with * [new branch] main -> main. If e end with git@vps.example.com: Permission denied (publickey), authentication never happen, so read server log with sudo journalctl -u ssh -n 20. Line wey read Authentication refused: bad ownership or modes for file /home/git/.ssh/authorized_keys mean say file mode no correct, because sshd dey ignore key file wey other users fit write.
Then remove shell access from the account.
command -v git-shell | sudo tee -a /etc/shells
sudo chsh -s "$(command -v git-shell)" gitgit-shell dey accept only the few commands wey Git dey send over SSH, so interactive login go stop with message instead of prompt:
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.Na the whole server be this. Database no dey, and web process wey you need upgrade no dey. Wetin you lose na everything wey forge dey provide: no browsing, no issue tracker, no pull requests, and no permission control per user. Every key for that file fit read and write every repository wey git user own.
Tier 1: cgit dey give you web view without database
cgit na CGI (common gateway interface) program wey dem write for C. Web server dey run am once for every request, e dey read repositories straight from disk, and e no dey store any state by itself. Ubuntu 24.04 carry am for universe component.
sudo apt update
sudo apt install -y cgit fcgiwrap nginx
sudo install -d -o www-data -g www-data /var/cache/cgitPoint am to the repository directory for /etc/cgitrc:
root-title=Git on example.com
css=/cgit.css
logo=/cgit.png
cache-size=1000
cache-root=/var/cache/cgit
snapshots=tar.gz zip
scan-path=/srv/gitscan-path dey scan that directory and list every repository wey e find, so new bare repo go show without extra configuration. cache-size na the number of cached pages, and caching no dey work while e be zero. Read wetin your package don already put for /etc/cgitrc before you add lines, because Debian and Ubuntu package dey ship some default settings by itself.
Each entry dey show the first line of the repository description file, so fresh bare repo go list itself as Unnamed repository; edit this file 'description' to name the repository. Fix am once for each repository:
echo 'Project X, internal tooling' | sudo -u git tee /srv/git/project.git/descriptionThe nginx site file, and how to check am
server {
listen 80;
server_name git.example.com;
root /usr/share/cgit;
try_files $uri @cgit;
location @cgit {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_param HTTP_HOST $server_name;
fastcgi_pass unix:/run/fcgiwrap.socket;
}
}sudo systemctl enable --now fcgiwrap.socket
sudo nginx -t && sudo systemctl reload nginx
systemctl show fcgiwrap.socket -p Listenroot /usr/share/cgit dey serve cgit.css and cgit.png as plain files, while try_files dey hand everything else to the CGI for /usr/lib/cgit/cgit.cgi. 502 page wey get connect() to unix:/run/fcgiwrap.socket failed (2: No such file or directory) inside /var/log/nginx/error.log mean say the socket unit no dey run, or e dey listen on another path. The systemctl show line dey print the path wey e dey use.
Two limits good make you know before you build on top am. cgit na read-only and e no get login, so everything under scan-path dey public: keep private repository out of that directory, or put HTTP basic authentication in front of the whole site. And CGI dey run as the web server user, so that user need permission to enter /srv/git and read each repository. Directory wey e no fit enter go show as empty index instead of error.
Tier 2: Forgejo anaa Gitea for issues anaa pull requests
Forgejo anaa Gitea na otu echiche: one Go binary wey dey serve web forge with users, organisations, issues, pull requests, releases, package registry anaa built-in CI system. Binary plus SQLite na the whole install, na why dem fit run for hardware wey GitLab no go even touch. The Compose file below na the one wey Forgejo documentation use, with the image tag wey e name as of August 2026.
networks:
forgejo:
external: false
services:
server:
image: codeberg.org/forgejo/forgejo:16
container_name: forgejo
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- forgejo
volumes:
- ./forgejo:/data
- /etc/localtime:/etc/localtime:ro
ports:
- '3000:3000'
- '222:22'docker compose up -d
docker compose ps
curl -sI http://127.0.0.1:3000 | head -1The curl line suppose print HTTP status line. Before you finish first-run setup, e fit be redirect go /install, and that still mean say service dey up. If container exit instead, usual cause na ownership: ./forgejo directory must belong to the UID (user id) for USER_UID, or the process no go fit write inside its own data directory. Docker Compose for VPS explain that file layout and volume ownership rule complete.
Two answers for setup page dey decide whether clone URLs go work. SSH port must be 222, because Compose file map host port 222 to container port 22, and domain must be the name wey people go actually type. If either one wrong, every repository page go show clone command wey go fail for everybody wey copy am. Both dey inside [server] section of app.ini afterwards, as SSH_PORT, SSH_DOMAIN anaa ROOT_URL.
For public instance, publish web port for loopback address only ('127.0.0.1:3000:3000') and put nginx in front of am for TLS (transport layer security). Gitea dey install the same way from gitea/gitea image, or as single binary with one systemd unit and one app.ini, and its current stable release na 1.27.1 as of August 2026.
Stay with SQLite as long as you fit. E keep the instance to one process and one file, and e survive reboot without extra service to supervise. PostgreSQL justify the extra cost when several people dey write at the same time, because SQLite dey serialise writes and long CI runs dey write constantly. Both projects fit move an existing instance to PostgreSQL later, so you no dey stuck with this decision.
Forgejo or Gitea: wetin really different
Dem two project get the same lineage. Gitea fork from Gogs for 2016. For late 2022, control of the Gitea domain and trademark pass to one company, Gitea Ltd, and some maintainers join Codeberg start Forgejo. Codeberg e.V., wey be non-profit association registered for Germany, dey publish Forgejo. For 2024, e move from MIT licence go GPLv3 (GNU general public license version 3). Gitea still use MIT licence, and commercial backing dey behind its development.
For everyday use, their feature sets dey close. But the migration path no be the same. Forgejo v10.0, wey come out for January 2025, na the last release wey fit take Gitea database directly, and na only from Gitea v1.22 or older. As of August 2026, Gitea dey on 1.27.1, so current Gitea instance no get supported in-place switch to Forgejo. Choose one before you put data inside am, and treat any later move as export plus re-import.
Short rule to help you choose. If governance matter to you, or you want make the project remain with non-profit, run Forgejo. If you want bigger install base and commercial support option, run Gitea. Both dey maintained openly and dem dey release often: Forgejo releases one stable release every three months and one LTS (long term support) release every year. As of August 2026, v16.0.2 na the current release, while v15.0.6 na the LTS.
Tier 3: wetin GitLab cost before e do anything
GitLab CE na different kind software. One instance na group of services wey dey work together: Puma for web application, Sidekiq for background jobs, PostgreSQL, Redis, Gitaly for repository access, and nginx for front. The Omnibus package install all of dem together. This make installation simple, but e make the minimum memory requirement high.
GitLab requirements page document 16 GB of RAM and 8 vCPU as the baseline for single node installation. E also list 8 GB as the low end for environment wey memory dey limited. The same page tell you make you disable swap, because swapping under load dey make the instance performance bad well well. Na the published figures as of August 2026. The requirements don increase over the years, so read the page again before you size server.
You go get real features for that resource budget: container registry, package registry, detailed permissions, compliance and audit features, plus CI wey dem don test for large scale. If nobody for your team fit name one thing from that list wey dem need this quarter, you dey pay for bigger VPS without getting anything in return.
SSH access model: one git user and plenty keys
Every tier for here dey authenticate the same way. Na one Unix account dey, wey dem name git, and every public key dey inside that account own ~/.ssh/authorized_keys. Na the key be authentication. Authorisation na the options wey you write before the key for the same line.
Plain key line go give whoever hold am anything wey that account fit do. Forced command go limit am to Git:
restrict,command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptoprestrict, wey OpenSSH 7.2 don support since, dey turn off port forwarding, agent forwarding, X11, and PTY (pseudo terminal) allocation with one word. command= go replace anything wey client request with the one wey you name, and Git still dey work because Git dey send im request for $SSH_ORIGINAL_COMMAND.
Forge dey write that file for you, and na this be the real difference between tier 0 and tier 2. Forgejo and Gitea dey rewrite authorized_keys with one line for every registered key. Each line get forced command wey name the key with im database id:
command="/usr/local/bin/forgejo --config=/etc/forgejo/app.ini serv key-3",no-port-forwarding,no-x11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere aliceNa that forced command dey turn one shared Unix account into permission per user: key-3 dey tell the forge which user dey connect, and e dey check that user against the repository before any objects move. No edit that file by hand for forge-managed box, because the database go rewrite am and your line go disappear. Deploy keys dey use the same mechanism. Deploy key na ordinary SSH key wey you register against one repository, usually read-only, while the forge dey do the check instead of sshd.
Two habits matter pass all the configuration wey we don talk. Issue one key for each person or machine, never use shared key, because to revoke shared key mean say you go rotate am for everybody at once. Also remove keys the same day person leave, because old key wey remain for that file na permanent login wey nobody dey monitor. Good SSH key management for server cover key types and passphrases, and everything there apply here without change. If the box new, the first ten minutes for new VPS na the correct thing to do before you put repositories for am.
Kya I run GitHub Actions for my own Git server?
You fit run workflows wey dem write with GitHub Actions syntax. But you no fit run GitHub itself. Forgejo Actions dey enabled by default since Forgejo v1.21, and e dey read workflow files from .forgejo/workflows for each repository. Gitea Actions dey work the same way and e dey read .gitea/workflows. Both need another program, the runner, wey you go install and register against your instance with token from the admin settings. Plenty published actions go run without change; but anything wey dey call GitHub API or expect GitHub-hosted infrastructure no go work.
Plan for two consequences. The runner dey start one container for every job, so e need container engine and separate memory budget. Na why e no suppose dey for the same 1 GB box with the forge. The runner also dey execute anything wey workflow file talk. Forgejo documentation state am plainly: the runner dey perform remote code execution. Give am separate host if you fit. At minimum, give am separate unprivileged user and registration token wey scope to one repository.
If your repositories go remain for GitHub and na compute for hardware wey you control you want, na different setup be that with different steps: a self-hosted GitHub Actions runner dey attach to GitHub repository and e no need any of these things. If you still dey consider wetin e cost to comot, wetin GitHub actually dey give you dey separate Git hosting from the network wey surround am.
Backups: repositories na only half of the state
A bare repository na directory, so if you copy am, you copy everything wey dey inside. A mirror clone from another machine na real backup, and e dey refresh in place:
git clone --mirror git@vps.example.com:/srv/git/project.git
cd project.git && git remote updateThat one dey pull every ref and every object. E no dey pull server-side hooks or the description file, so if you dey use hooks, keep file-level copy of the directory too.
A forge dey keep issues, pull requests, users, keys and permissions for database. If you copy repositories alone, you go lose all those things. Both projects release a dump command wey dey write the database, repositories, configuration and attachments into one archive:
sudo -u git forgejo dump -c /etc/forgejo/app.ini -f /var/backups/forgejo-dump.zipFor Docker, the same command dey run inside the container. The configuration path depend on the image, so check am before you type:
docker compose exec server ls /data/gitea/conf
docker compose exec -u git server forgejo dump -c /data/gitea/conf/app.iniRun am as the user wey own the data, and write the archive to a directory wey that user fit write to. Then copy the archive comot from the server, because backup wey only dey for the machine wey you dey back up no be backup. Restoring na the step wey people dey skip: unpack one dump onto a spare box now, so you go learn the procedure when everywhere calm instead of during outage.
Pick by scenario
One person wey get laptop and VPS, and no need browse code: use bare repositories over SSH. No extra service dey run, and nothing dey need upgrade.
The same setup, plus you wan read code for browser and send links to am: add cgit. Database still no dey, and nothing still dey resident.
Team wey dey review each other's code and track issues: use Forgejo or Gitea for system wey get 2 GB of RAM or more. Move the CI runner go second machine when jobs don become serious.
Organisation wey need container registry and audit trails, and get 16 GB to use for the server: use GitLab. If budget no reach that level, no start am.
Moving from the first three tiers go higher one cheap, because repositories for all of dem na ordinary Git directories for disk. Start with the lowest tier wey fit do the work. If you dey decide wetin else deserve space for the same server, shortlist of wetin worth self-hosting put Git server beside the other services wey dey compete for that RAM.
FAQ
Small VPS fit run Forgejo or Gitea?
Yes, for small team, if na SQLite dey use and no other heavy thing dey run for the server. Gitea documentation talk say 1 GB of RAM and 2 CPU cores normally dey enough for small teams and projects, and Forgejo na fork of Gitea with similar requirement. No add PostgreSQL or CI runner to that machine. If the service disappear without error for im own log, run sudo dmesg -T | grep -i oom: if one line name the process wey dem kill, kernel out of memory killer don stop am. Bigger plan na the solution, no be tuning flag.
Wetin be the difference between Forgejo and Gitea?
Dem get shared codebase history and most features. Gitea fork from Gogs for 2016, and Forgejo fork from Gitea for late 2022 after control of the Gitea trademark move go one company. Codeberg e.V., wey be non-profit for Germany, publish Forgejo under GPLv3; Gitea remain MIT licensed with commercial backing. The main practical difference na the migration path. Forgejo v10.0, from January 2025, na the last release wey fit take Gitea database directly, and na only from Gitea v1.22 or older. So current Gitea instance no get supported in-place switch.
I fit run GitHub Actions workflows for self-hosted Git server?
Forgejo Actions and Gitea Actions both run workflows wey dem write with GitHub Actions YAML syntax, and dem read am from .forgejo/workflows and .gitea/workflows. You go install separate runner program and register am against your instance. Plenty published actions dey work without change, but anything wey dey call GitHub API no go work. Runner dey execute arbitrary code from your repositories and start one container for every job. So give am im own host, or at least im own unprivileged user, and no run am for 1 GB server wey forge already dey use.
How I fit back up self-hosted Git server?
For bare repositories, git clone --mirror from another machine go copy every ref and object, while git remote update inside that mirror go refresh am. For Forgejo or Gitea, repositories na only part of the state because issues, pull requests, users, and keys dey inside the database. Use the built-in dump, sudo -u git forgejo dump -c /etc/forgejo/app.ini, or run the same command inside the container for Docker installation. Copy the archive comot from the server, then restore one for spare machine once, so you go know say the procedure dey work.