Self-hosting GitLab is a common choice for development teams that want control over their code, their CI/CD pipelines and where their data lives. The first installation is usually easy: one virtual machine, the official package and a weekend. The trouble starts a year later, when the team has doubled, pipelines run all day and the web interface feels slow every morning.
Sizing GitLab well from the start, and knowing which signals mean it is time to grow, saves a painful emergency migration. This guide covers how to use GitLab’s own sizing guidance, where the load really comes from and when a bigger server makes sense.
Start With the Reference Architectures
GitLab publishes reference architectures for self-managed installations. They describe tested deployments by expected load, expressed as requests per second (RPS) with an equivalent user count, starting at up to 20 RPS or 1,000 users and going up to 1,000 RPS or 50,000 users.
Two points from the documentation are especially useful for smaller teams. First, environments serving 2,000 or fewer users are generally advised to use a standalone, non-highly-available setup, protected by automated backups, rather than a complex cluster. Second, RPS is a better guide than headcount. A team of 200 engineers with heavy automation can generate more load than a team of 800 that mostly reviews code in the browser, so measure or estimate requests per second before choosing a size.
The reference architectures also note when components need adjusting, for example more Gitaly capacity for very large repositories or more Sidekiq workers for heavy CI/CD throughput. Read those notes before assuming the default sizing fits your workload.
Where the Load Really Comes From
A GitLab installation is a collection of services, and each one stresses the server differently.
|
Component |
What it does |
What to watch |
|
Puma (web and API) |
Serves the interface and API calls |
Request latency, worker saturation |
|
Sidekiq |
Runs background jobs such as pipeline processing and notifications |
Queue length and job wait time |
|
Gitaly |
Handles Git operations on repositories |
CPU during clones and pushes, disk latency |
|
PostgreSQL |
Stores issues, merge requests and metadata |
Slow queries, connections, cache hit ratio |
|
Redis |
Caching and job queues |
Memory usage, latency |
On a single server, these components compete for the same CPU cores, memory and disks. API traffic from CI pipelines and integrations is often the largest share of requests, and it tends to arrive in bursts when many pipelines start at once. Integrations that poll the API every few seconds, such as chat bots and dashboards, add steady background load that is easy to overlook. Measure before guessing: GitLab exposes Prometheus metrics for its components, which gives a useful view of request rates, queue lengths and latency.
Keep CI Runners Off the Main Server
The single most common sizing mistake is running CI jobs on the same machine as GitLab itself. A build that compiles code, runs tests or builds container images can use every available core for minutes at a time, and the web interface slows down for everyone while it runs.
Install GitLab Runner on separate machines. Runners register with the GitLab instance and pick up jobs, so they can be added or removed without touching the main server. Size runners for your heaviest pipelines and set sensible concurrency limits, so that a busy afternoon queues jobs instead of overloading hosts. Ephemeral runners that start clean for each job also reduce problems with leftover files and cached credentials.
Runner hosts have different needs from the GitLab server. They benefit from fast local disks for build caches and container layers, and they can often be scaled down outside working hours, which makes them a good fit for cheaper or temporary capacity.
Storage, Backups and Upgrades
Repositories, CI artifacts, container images, uploads and package registries grow at different rates. Artifacts and container images usually grow fastest, so set expiry rules for artifacts and cleanup policies for registries early. Object storage for large files keeps the main disks focused on repositories and the database.
Fast, low-latency storage matters for Gitaly and PostgreSQL. NVMe drives make a noticeable difference for Git operations on large repositories and for database performance during busy periods. Keep an eye on the database itself as well: large instances benefit from monitoring table bloat and long-running queries, especially after major upgrades.
GitLab’s backup tooling covers the database and repositories, but configuration files and secrets need to be backed up separately, and without the secrets file some encrypted data cannot be restored. Test a restore on a separate machine at least twice a year. Plan upgrades as well: GitLab releases frequently, and following the supported upgrade paths is far easier when you stay reasonably current.
When to Move to a Bigger Server
Watch for clear signals rather than general complaints:
- Sidekiq queues that stay long after the morning peak.
- Web or API latency that rises when many pipelines start at the same time.
- Git clone and push times growing as repositories grow.
- Memory pressure or swapping on the main server.
- Backups that no longer fit comfortably into the nightly window.
When several of these appear together, the server is likely too small. Many teams move from a virtual machine to bare metal at this point, because dedicated hardware removes noisy neighbors and gives predictable disk performance. A dedicated EPYC server with many cores and plenty of memory channels suits the GitLab pattern well: Puma, Sidekiq, Gitaly and PostgreSQL all run in parallel, and a large core count lets them work side by side without starving each other. Unihost and similar providers rent such servers with NVMe storage, which helps Gitaly and the database.
Before migrating, compare the reference architecture that matches your measured RPS with the planned server, and keep CI runners on separate machines regardless of how large the new host is. Schedule the move for a quiet period, announce a short freeze on merges and keep the old instance read-only for a few days in case something was missed.
A self-hosted GitLab stays fast when it is sized by real load rather than headcount. Use the reference architectures as the starting point, keep CI jobs away from the main server, control storage growth, test restores and watch the signals that show when it is time to scale up.



