Which self-hosted Git server fit run for 1 GB VPS?
Compare bare repos over SSH, cgit, Forgejo, Gitea and GitLab by RAM. See which one fit run for your 1 GB VPS, plus the real memory gotchas.
Which self-hosted Git server you suppose run
Self-hosted Git server no be one product, and the RAM (random access memory) for your VPS na im 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. Anything 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.
There are four steps. Bare repository over SSH, with nothing listening wey no dey listen before. cgit, na fast read-only web view wey no get database. Forgejo or Gitea, na full forge with accounts, issues and pull requests for a few hundred megabytes. GitLab, wey dey expect server many times bigger than the others.
Decide based on the work wey you need do, then compare the memory figure with the plan wey you dey pay for.
How much RAM each option really needs
Only two of these projects publish a hardware figure. Treat a published figure as the minimum, not a promise. Measure your own instance after e start, 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 usually enough for small teams and projects. E also mention say Raspberry Pi 3 dey enough for small workloads. GitLab document 16 GB as the baseline for one-node installation. E document 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, and e dey behave like Gitea, so the Gitea figure na the closest published guide wey you get.
Wetin this mean for 1 GB VPS: bare repositories and cgit go fit with memory left, because neither of dem dey 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). This mean say 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 for 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 any 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 this be the commonest 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 go 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 show 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 through 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 everything wey server need be that. No database dey, and no web process dey wey you need upgrade. Wetin you lose na everything 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 each request, e dey read the repositories straight from disk, and e no dey keep any state by itself. Ubuntu 24.04 get 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 number of cached pages, and caching go remain off as long as 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 defaults of their own.
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 this 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 it
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 pass everything else go 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 socket unit no dey run, or e dey listen for another path. The systemctl show line dey print the path wey e actually dey use.
You need know two limits before you build on top of am. cgit na read-only and e no get login, so everything under scan-path dey public: keep private repository outside 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 traverse /srv/git and read every repository. If directory no allow am enter, e go show as empty index instead of error.
Tier 2: Forgejo or Gitea for issues and pull requests
Forgejo and Gitea na the same idea: one Go binary wey dey serve web forge with users, organisations, issues, pull requests, releases, package registry and built-in CI system. Binary plus SQLite na the complete 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 October 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 one still mean say the service dey up. If the container exit instead, the usual cause na ownership: the ./forgejo directory must belong to the UID (user id) for USER_UID, or the process no fit write to its own data directory. Docker Compose for VPS explain that file layout and volume ownership rule complete.
Two answers for the setup page decide whether clone URLs go work. The SSH port must be 222, because the Compose file map host port 222 to container port 22, and the domain must be the name wey people go actually type. If either one wrong, every repository page go offer clone command wey go fail for anybody wey copy am. Both dey inside the [server] section of app.ini afterwards, as SSH_PORT, SSH_DOMAIN and ROOT_URL.
For public instance, publish the web port for loopback address only ('127.0.0.1:3000:3000'), then put nginx in front of am for TLS (transport layer security). Gitea install the same way from the docker.gitea.com/gitea image wey its documentation use, or as one binary with one systemd unit and one app.ini. Its current stable release na v28.1.0, published 6 October 2026. With v28.0.0 at the end of September, the project drop the old 1. prefix, so the release after 1.27 na 28, no be 1.28.
Remain on SQLite as long as you fit. E keep the instance to one process and one file, and e survive reboot without any 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 existing instance go PostgreSQL later, so you no dey stuck with this decision.
Forgejo or Gitea: wetin really different
Dem get the same origin. Gitea fork from Gogs for 2016. For late 2022, control of the Gitea domain and trademark move go one company, Gitea Ltd, and some maintainers together with 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 dey use MIT licence, and commercial backing dey support the development.
For everyday use, the feature sets dey close. But the migration path no be the same. Forgejo v10.0, wey release for January 2025, na the last release wey fit take Gitea database directly, and na only from Gitea v1.22 or older. As of October 2026, Gitea dey for v28.1.0, 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 and re-import.
Here be short rule for choosing. If governance matter to you, or you want make the project remain with non-profit, run Forgejo. If you want larger install base and commercial support option, run Gitea. Both projects dey maintained openly and dem release often. Forgejo dey ship one stable release every three months and one LTS (long term support) release every year. As of October 2026, v16.0.5 na the current release, while v15.0.9 na the LTS. Forgejo support v16 reach 29 October 2026 and v15 reach 15 July 2027, so check the releases page before you pin major version.
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 the web application, Sidekiq for background jobs, PostgreSQL, Redis, Gitaly for repository access, and nginx for front. The Omnibus package dey install all of dem together, so installation easy but memory requirement high.
GitLab requirements page document 16 GB of RAM and 8 vCPU as the baseline for single node installation. E also name 8 GB as the low end for environment wey get limited memory. The same page tell you make you disable swap, because swapping under load dey reduce instance performance badly. Na the published figures as of October 2026, and dem don increase over the years, so read the page again before you size server.
You go get useful things for that budget: container registry, package registry, fine-grained permissions, compliance and audit features, and CI wey dem don test for large scale. If nobody for your team fit name anything from that list wey dem need this quarter, you dey pay for bigger VPS without getting any benefit.
SSH access model: one git user and plenty keys
Every tier for here dey authenticate the same way. Na one Unix account dey named git, and every public key dey go inside that account own ~/.ssh/authorized_keys. The key na wetin authentication use. Authorisation na the options wey you write before the key for the same line.
Plain key line give the person wey hold am anything wey that account fit do. Forced command reduce am to Git:
restrict,command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptoprestrict, wey dey available since OpenSSH 7.2, dey turn off port forwarding, agent forwarding, X11, and PTY (pseudo terminal) allocation with one word. command= replace anything wey client request with the one wey you specify, and Git still dey work because Git dey send the 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 its 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 make one shared Unix account work with per-user permission: key-3 tell the forge which user dey connect, and e check that user against the repository before any objects move. No edit that file by hand for forge-managed box, because forge dey rewrite am from the database and your line go disappear. Deploy keys come from the same mechanism. Deploy key na ordinary SSH key wey you register against one repository. E usually dey read-only, and na the forge do the check instead of sshd.
Two habits matter pass all the configuration wey we talk about. Create one key for each person or machine. No use shared key, because to revoke shared key mean say you go rotate am for everybody at the same time. 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 for there still 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.
Can I run GitHub Actions for my own Git server?
You fit run workflows wey dem write with GitHub Actions syntax. You no fit run GitHub itself. Forgejo Actions don dey enabled by default since Forgejo v1.21, and e dey read workflow files from .forgejo/workflows inside each repository. Gitea Actions dey work the same way and e dey read .gitea/workflows. Both need another program, wey dem call runner. You must install and register the runner against your instance with token from 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 good put am for the same 1 GB box wey dey run the forge. The runner go execute anything wey workflow file command. Forgejo documentation talk this clearly: the runner performs remote code execution. Give the runner its own host if you fit. At minimum, give am its own unprivileged user and registration token wey scope to one repository.
If your repositories still dey GitHub and you only want make computation run for hardware wey you control, na different setup 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 leaving go cost you, wetin GitHub actually dey give you separate the Git hosting from the network wey surround am.
Backups: repositories na only half of the state
Bare repository na directory, so if you copy am, you copy everything wey dey inside. Mirror clone from another machine na real backup, and e refresh in place:
git clone --mirror git@vps.example.com:/srv/git/project.git
cd project.git && git remote updateThat one go pull every ref and every object. E no go pull server-side hooks or the description file, so if you dey use hooks, keep file-level copy of the directory too.
Forge dey keep issues, pull requests, users, keys, and permissions for database. If you copy repositories alone, you go lose all of dem. Both projects release dump command wey dey write 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, na inside the container you go run the same command. Configuration path dey 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 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 spare box now, so you go learn the procedure when everywhere calm, instead of during outage.
Scenario wey fit match
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, but you want read code for browser and send links to am: add cgit. Database still no dey, and nothing dey stay resident.
Team wey dey review each other code and track issues: use Forgejo or Gitea for server wey get 2 GB RAM or more. Move CI runner go second machine once jobs become serious.
Organisation wey need container registry and audit trails, and get 16 GB RAM budget for server: use GitLab. If budget no reach that level, no start am.
Moving from the first three tiers go higher one no cost much, 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 things wey worth self-hosting put Git server near the other services wey dey compete for that RAM.
FAQ
1 GB VPS fit run Forgejo or Gitea?
Yes, for small team, if na SQLite dey run and no other heavy service dey that machine. Gitea documentation talk say 1 GB 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 its own log, run sudo dmesg -T | grep -i oom: if one line name the process wey dem kill, na kernel out of memory killer kill am. The solution na bigger plan, no be tuning flag.
Wetin be the difference between Forgejo and Gitea?
Dem share codebase history and most features. Gitea fork from Gogs for 2016, and Forgejo fork from Gitea for late 2022 after control of Gitea trademark move go one company. Codeberg e.V., wey be non-profit for Germany, publish Forgejo under GPLv3; Gitea still use MIT license and get commercial backing. The practical difference na migration path. Forgejo v10.0, wey release for 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 each job. So give am its own host, or at least its own unprivileged user. No run am for 1 GB server wey already dey run the forge.
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 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.