The documented memory floor for this software is not the figure to build on, and the backup command does not capture the one file without which a restore is worthless. Those two facts account for most unhappy GitLab deployments. Here is the install, the single configuration file that controls everything, and where the runners belong.
GitLab is not a Git web interface with extras. The omnibus package installs Puma, Sidekiq, Gitaly, Redis, PostgreSQL, Nginx, Prometheus exporters and a container registry, all supervised together. That is why it does what it does, and why the resource floor is so much higher than the alternatives.
Be Sure You Want GitLab
Start here, because the honest answer is often no. If what you need is repository hosting with a web interface, issues and pull requests, a lighter forge does it on a fraction of the hardware and takes minutes to upgrade rather than an evening.
GitLab earns its footprint when you want the integrated set: CI/CD with runners, a container registry, a package registry, environments and review apps, merge trains, and the compliance and approval features around them. If you would otherwise assemble those from four separate tools, the single system is the better trade.
Our guide to self-hosting Forgejo covers the lighter option, and our walkthrough for running your own Git server covers the case where a bare repository and SSH keys are genuinely enough.
Sizing, and Why 4 GB Is Not Enough
| Scale | vCPU / RAM / SSD | Monthly on per-resource pricing |
|---|---|---|
| Minimum usable, up to 10 users | 4 / 8 GB / 100 GB | $18.88 |
| Comfortable, 10-50 users | 8 / 16 GB / 200 GB | $37.76 |
| With CI runners and registry | 8 / 16 GB / 400 GB | $39.76 |
| Busy, 100+ users | 16 / 32 GB / 500 GB | $76.52 |
Rates are MassiveGRID's $2.87 per CPU core, $0.80 per GB of RAM and $0.01 per GB of SSD per month, less 20% annually. GitLab's own documentation puts the floor at 4 GB of RAM, and that floor buys an instance that runs and swaps under any real use. Treat 8 GB as the practical minimum.
The disk numbers assume the registry and CI artefacts stay on the same volume, which is what fills disks unexpectedly. Artefacts and container images accumulate quietly until a push fails, and the fix is retention policy rather than more disk.
Install the Omnibus Package
sudo apt update && sudo apt install -y curl openssh-server ca-certificates tzdata perl
curl -fsSL https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo EXTERNAL_URL="https://gitlab.example.com" apt install -y gitlab-ce
sudo gitlab-ctl status
EXTERNAL_URL at install time is the one value you want correct from the start. GitLab writes clone URLs, webhook targets, registry addresses and OAuth callbacks from it. Changing it later works but touches every one of those, and existing runners and webhooks need updating.
Read the install script before piping it to a shell, as with any such one-liner. Everything after this point is done through one configuration file.
One File, and One Command
sudo editor /etc/gitlab/gitlab.rb
sudo gitlab-ctl reconfigure
sudo gitlab-ctl tail nginx
gitlab.rb is the whole configuration surface, and reconfigure regenerates every service's own config from it. Editing a component's native config file directly is pointless, because the next reconfigure overwrites it. This is unusual and it is the single most useful fact about operating GitLab.
Settings worth attention on a modest server: trim Puma workers and Sidekiq concurrency, since defaults assume a larger machine; disable the bundled Prometheus stack if you already have monitoring, which returns a noticeable amount of memory; and set the registry and artefact paths onto whichever volume you intend to grow.
Runners Go Somewhere Else
CI runners on the GitLab host is the arrangement that seems economical and then takes the server down. A build that consumes memory or saturates the disk competes directly with the application, so a heavy pipeline makes the web interface unusable for everyone.
Register runners on separate instances. A small VPS per runner is cheap next to the cost of GitLab being slow whenever anyone pushes, and it lets you scale build capacity without touching the application server.
# on the runner host
sudo gitlab-runner register --url https://gitlab.example.com --token glrt-xxxx
sudo gitlab-runner list
Use the Docker executor rather than shell, so each job gets a clean environment and a bad build cannot leave state behind for the next one. Set concurrency to what the runner host can actually take, and set artefact and cache expiry, because both grow without limit by default.
Backups, and the Secrets File
sudo gitlab-backup create STRATEGY=copy
sudo cp /etc/gitlab/gitlab-secrets.json /backup/
sudo cp /etc/gitlab/gitlab.rb /backup/
All three lines. gitlab-backup create captures repositories, the database, uploads and artefacts. It does not capture gitlab-secrets.json, and that file holds the keys used to encrypt CI variables, two-factor secrets and stored tokens.
Restoring a backup without the matching secrets file gives you an instance where the repositories are intact and every encrypted value is unreadable: CI variables gone, two-factor broken for every user, integrations dead. This is the most consequential GitLab operational mistake and it is one line in a backup script.
Send the copies off the server. Backup services use block-level incremental backups with AES-256 encryption at $0.01 per GB, and treat the secrets file as the credential it is, not as one more file in the archive.
Upgrades Follow a Path
GitLab does not support arbitrary version jumps. Background migrations must finish before the next major step, so going from an old release to current means following the documented upgrade path through specific intermediate versions in order.
Check the upgrade path for your exact starting version before beginning, take a backup at each step, and wait for background migrations to report complete before moving on. Skipping ahead produces a database in a state the application cannot repair, and the recovery is a restore. Our guide to upgrading self-hosted applications covers rehearsing this on a copy first, which for GitLab is not optional advice.
Infrastructure for a System That Gates Deployment
Once CI runs through GitLab, its availability is your ability to ship. That changes what the hosting decision is worth.
MassiveGRID runs Proxmox high-availability clustering with automatic failover over Ceph storage replicating every block three times across independent NVMe drives, behind a 100% uptime SLA, so a host failure restarts the instance rather than stopping releases. A Linux VPS at the per-resource rates above covers the application and the runners separately, high-availability cloud servers add managed operations from $9.99 plus $5 management, and DevOps support covers the upgrade path if nobody wants to own it.
Servers can be ordered across a partner footprint of more than 700 datacenters in 85 metros, 30 countries and six continents, with auto-provisioning in New York, London, Frankfurt and Singapore.