When a single GitLab server is enough for up to 2,000 users
One self-managed GitLab server can carry source control, code review and CI/CD for a population of up to 2,000 users, provided the workload stays within what a single machine can serve and the recovery plan is proven. The decision is less about headcount than about request load, repository size and CI concurrency, so start by checking those against the published reference architectures rather than assuming a single box is enough or too small. The Linux package is the method most teams will use for this, because it bundles PostgreSQL, Redis and Sidekiq on the same host, which keeps the moving parts few and the upgrade path well documented.
GitLab's reference architectures start at 1,000 users and 20 requests per second, and the reference architectures documentation recommends a high availability strategy for 3,000 or more users. Between those points sits the zone this page addresses, where a single Linux package server is a defensible choice if your measured load and sizing support it. Confirm the current sizing guidance for your user count and expected API traffic in the GitLab reference architectures documentation before you commit hardware.
This page covers the CPU, memory and storage baselines for that single server, how automated backups become the recovery plan, and the signals that tell you it is time to move to the 3K high availability design. Read it as a decision guide: if you can state your user count, peak request rate, repository footprint and CI job volume, you can decide whether the single-node path is sound, and you can identify which thresholds would trigger a redesign before users notice.
Which teams fit a single node and which do not
A small engineering group with modest concurrent Git activity and light pipeline demand is the clearest fit for one Linux package server. Headcount alone rarely settles the question, but a team of this size usually sits near or below the smallest published reference architecture, which starts at 1,000 users and 20 requests per second.
- Stable user base: Accounts are known, onboarding is infrequent, and growth is expected to stay within the ceiling of this topic.
- Light CI usage: A handful of runners with moderate concurrency leaves headroom on CPU and memory for peaks.
- Internal ownership: An existing platform or DevOps group can own upgrades, backups and restores without a dedicated database administrator.
- Tolerable downtime: The business accepts a maintenance window for upgrade stops and a restore from the last backup after a failure.
Before you commit, confirm the user count, peak requests per second and pipeline concurrency against the reference architecture guidance, and confirm your team can staff each upgrade stop.
CI load is often the constraint that surprises teams. Many concurrent jobs, frequent runner requests to the API and wide artifact traffic all add to requests per second, and they land on the same host that serves the web application and Sidekiq background jobs.
- Concurrent pipelines: High parallelism pushes API and Sidekiq work onto one host at the same time.
- Runner fleet growth: More runners increase job requests, so runner count and autoscaling settings belong in the sizing discussion.
- Peak windows: Release days and merge bursts matter more than averages, so measure peak load rather than a typical day.
- Artifact and cache storage: Job artifacts consume disk and I/O on the server unless you plan separate storage for them.
- Pipeline design: Large matrix builds and wide fan-outs multiply load even for a small team.
Measure peak requests per second from your current logs or metrics, then compare it with the reference architecture targets. The GitLab Runner documentation is the natural next read for runner settings that affect API load.
Repository size and access patterns affect the Gitaly service more than user count does. A large monorepo with heavy clone, fetch and merge request activity places sustained I/O and memory pressure on the server that holds the repositories, and a single host has no spare capacity to absorb that alongside everything else.
- Repository size: Long histories and many branches increase disk use and the cost of each Git operation.
- Clone and fetch traffic: Many developers cloning or fetching at the same time concentrates load on storage and CPU.
- Shallow and partial clones: These reduce client transfer, but their effect on server load needs testing with your own repositories.
- Shared resources: Repository work competes with the web and background processes on the same host.
- Growth over time: A repository that fits today can outgrow its storage and I/O headroom during the life of the server.
Measure repository size, clone frequency and fetch volume, then review the sibling topic on scaling GitLab for large monorepos before you decide that one server is enough.
In regulated environments the question shifts from capacity to recovery. A single node is one failure domain, so recovery depends on backups and a restore procedure someone has actually run. Risk owners and auditors usually ask for a recovery time objective and a recovery point objective, and the business must set those values before the design is accepted.
- Recovery objectives: Confirm the recovery time and recovery point the business requires, then test whether one host can meet them.
- Restore testing: A backup that has never been restored into a separate environment is unproven, so schedule restore drills and keep the results.
- Backup scope: Repositories, the database, uploads and configuration secrets all need to be included and protected.
- Disaster recovery: GitLab Geo provides a warm-standby secondary site in another region, which a single node does not include and which requires Premium or Ultimate.
- Offline constraints: Air-gapped installs need upgrades and scanner data delivered through controlled channels, which adds steps to every upgrade stop.
Ask your risk and compliance owners to document the objectives and the restore test evidence, then compare both against what one host can deliver.
Sizing options and when each fits
This table compares the ways to install GitLab on one server and the sizing baseline each one starts from. Read it to choose the install method first, then confirm the server baseline against the reference architecture and your own workload. The main takeaway is that the Linux package on a virtual machine is the documented starting point for a single node, while Kubernetes options leave you without a matching baseline.
| Option | When it fits | Trade-off |
|---|---|---|
| Linux package on one VM | Your team runs Linux servers and wants the most mature install path for a single node. | Upgrades, backups and reconfiguration are yours to schedule. The package runs PostgreSQL, Redis and Sidekiq on the same host. |
| Docker container | Your platform team already operates container hosts and wants a self-contained image. | Volume mounts, container storage and backup paths need careful mapping, and recovery depends on that layout. |
| Helm chart on Kubernetes | You already run a Kubernetes cluster with mature storage and backup tooling. | Reference architectures do not run PostgreSQL or Redis inside Kubernetes, so no documented sizing baseline matches this layout. |
| GitLab Operator | Your platform team standardizes on Kubernetes operators for lifecycle management. | Carries the same stateful-services gap as the Helm chart, plus an extra control layer to upgrade and troubleshoot. |
| Reference architecture, 1,000 users | The starting point for sizing one server, and the baseline this page measures against. | Documented for 1,000 users at 20 requests per second. Sizing toward 2,000 users needs load testing and your own usage data. |
| Move to 3K HA design | Users, CI concurrency or recovery targets outgrow what one host can meet. | Documented HA guidance starts at 3,000 users. Plan the move before growth or recovery needs force it. |
How to set up a single-node GitLab server step by step
This sequence takes a team from a sizing decision to a single Linux package GitLab server that is backed up off-host, hardened, load tested and has a documented upgrade plan before users arrive.
Confirm the reference architecture and hardware baseline
Start by matching your user count and expected API load to the published reference architectures, then confirm the CPU, memory and storage baseline for the single-node layout with your platform team. Check the current GitLab reference architecture page, since the baseline values change between releases, and write the figures into the change record. Done when the architecture, the hardware request and the sign-off from the infrastructure owner are recorded together. Ask any partner to show how the baseline was derived from your own user and CI numbers, not from headcount alone.
Provision the virtual machine in your infrastructure
Create the VM in your cloud account, data center or air-gapped network with the agreed CPU, memory and disk, a fixed private address and a DNS record for the external URL. Restrict inbound access to the HTTPS, SSH and Git SSH ports your users need, and place backup storage on a separate volume or target. Confirm the operating system is one the Linux package supports for your target GitLab release. Done when you can reach the host over SSH, resolve the DNS name from the user network and see the disk sizes you requested.
Install GitLab with the Linux package
Install the Linux package for your chosen release following the installation methods guidance, which describes it as the most mature and scalable method. The package bundles PostgreSQL, Redis and Sidekiq, so a single node needs no separate database or cache build. Pin the exact version you plan to run and keep a copy of the installer checksum in your records. Done when the omnibus reconfigure completes without errors and the GitLab sign-in page loads over the local address.
Configure gitlab.rb and the external URL
Edit the gitlab.rb configuration file to set external_url, the backup path, SMTP settings, log retention and the storage locations you planned in step two. Store the file and its secrets file under version control or your configuration management tool, and restrict read access to the administrators. Run the reconfigure command after each change and confirm the change took effect. Done when gitlab-ctl status shows every service running, the external URL redirects to HTTPS with a valid certificate, and a test email arrives.
Set up automated backups and off-host storage
Configure the backup task on a schedule and send archives to storage that is separate from the GitLab host, such as an object store or backup server in another failure domain. Back up the secrets files in gitlab.rb and the configuration directory on their own schedule, because a database backup alone cannot restore encrypted data. Define retention, encryption at rest and who may read or restore the archives. Done when a scheduled backup completes, an archive lands off-host, and a restore has been rehearsed on a separate test VM and the users, projects and CI variables match the source.
Apply the hardening baseline
Work through the hardening guidance in stages, starting with application settings such as sign-up restrictions, default project visibility and SSH key rules, then CI/CD settings, then the operating system and gitlab.rb items. Record each setting you adopt and each one you defer with a reason, since the hardening guidance is a work in progress to be rolled out gradually. Map the results to your control framework where your auditors need evidence. Done when the settings review is signed off by the platform owner and a second administrator has confirmed the changes in the admin area.
Load test before go-live
Run a load test against the instance using the GitLab Performance Tool or an equivalent method, targeting the request rate of the reference architecture you selected in step one. Include representative Git operations, web sessions and CI job volume, and check CPU, memory, disk latency and Sidekiq queue depth during the run. Compare results with the targets in the reference architecture documentation before users are onboarded. Done when the test passes at the agreed target, the results are saved, and any bottleneck has a documented remediation or a decision to accept it.
Record the upgrade plan with required upgrade stops
Write an upgrade runbook that names the current version, the next required upgrade stops and the backup taken before each stop, using the upgrade paths documentation. Schedule a maintenance window, a test upgrade on a restored copy and a rollback decision point, and subscribe the team to the maintenance policy so patch releases are tracked. Also note the growth signals that would trigger the move to the 3,000-user high availability design. Done when the runbook is approved, a test upgrade on the restored copy has completed, and the next required stop is on the team calendar.
Why automated backups are the recovery plan on one node
On a single node, the backup task is your primary recovery path. There is no standby server to fail over to and no second database replica, so if the host, its disk or its PostgreSQL data is lost, the backup archive is what you restore from. The GitLab backup and restore documentation at GitLab backup and restore covers the gitlab-backup task, what it captures (repositories, the database, uploads, artifacts and other application data), how restores are run and the version rules that apply when restoring. Read it against the exact GitLab version you install, because the commands and exclusions change between releases.
The backup task does not capture everything you need. The secrets files, principally /etc/gitlab/gitlab-secrets.json and /etc/gitlab/gitlab.rb, must be copied separately and stored securely, because encrypted data in the database cannot be read after a restore without the matching keys. Keep the copies off the host, in a location with its own access controls and retention, and confirm which configuration files your environment depends on. Off-host storage matters for the same reason: a backup kept on the same disk as the instance fails together with it. If you use object storage for uploads or artifacts, confirm how those objects are backed up and restored, since they may sit outside the archive.
Test restores before you rely on them. Restore into a separate host at least periodically, confirm that repositories open, pipelines show history, and users can sign in, and time the procedure so you know how long recovery takes. Confirm the backup schedule, retention period and recovery time and recovery point objectives with your own risk owners, since those are business decisions the documentation cannot make for you. If those objectives tighten beyond what nightly archives can meet, Geo is a separate option: it provides a warm-standby secondary site in another region that is continuously synchronized, and it is available in the Premium and Ultimate tiers. Geo adds infrastructure and operating effort, so cost it against the objectives rather than assuming it is needed.
Security and operational risks of one server
A single Linux package server concentrates every role in one failure domain and leaves patching, hardening and access control entirely to your team. The cards below name the controls that keep those risks manageable and what to confirm in your own environment before go-live.
Single point of failure
Rails, Gitaly, PostgreSQL, Redis and Sidekiq all run on one host, so a disk, kernel or hypervisor failure stops source control, code review and CI/CD at once. Recovery depends on restoring the latest backup onto replacement hardware, which is a rebuild rather than a failover. Confirm the restore time your teams can tolerate, who performs the rebuild, and whether a standby VM or spare host is available. Test a restore on a separate machine before you rely on it.
Patch timing on a monthly cadence
GitLab ships a minor release on the third Thursday of each month and patch releases twice a month, so a single server needs a standing maintenance window that absorbs this rhythm. Each upgrade also requires a backup taken immediately beforehand and a check that required upgrade stops are respected. Confirm who approves patch windows, how long a maintenance window can run for your users, and where the upgrade is rehearsed first.
Security fix backport window
GitLab backports bug fixes only to the current stable release, while security fixes also reach the previous two monthly releases. Running an older minor version to avoid disruption therefore means you receive security fixes only within that two-release window, and anything older falls outside it. Confirm which release line your instance is on, how quickly your team can move onto a supported line, and who monitors release announcements for security content.
Hardening as a gradual rollout
GitLab's hardening guidance covers application settings, CI/CD, gitlab.rb, the operating system and NIST SP 800-53 alignment, and it states the guidance is a work in progress to be rolled out gradually. A single server means one configuration to drift, so each setting needs an owner, a change record and a test before it reaches production. Confirm which controls your compliance obligations require, how exceptions are documented, and how configuration is kept in version control.
Identity integration and access
Self-managed GitLab supports LDAP, SAML, generic OAuth 2.0, OmniAuth providers, Kerberos, JWT, smart card authentication and SCIM provisioning. On one server, the choice of method decides how joiners, movers and leavers are removed from source control and CI, so local accounts left behind become an exposure. Confirm which identity provider is authoritative, whether SCIM deprovisioning is in scope, and who reviews accounts that exist outside corporate identity.
Runner isolation and CI trust
GitLab Runner executes CI/CD jobs, and a runner that shares the GitLab host can expose secrets, the filesystem and the Rails database to untrusted pipeline code. Shell and Docker executors differ in how much isolation they give, and autoscaling runners on separate hosts keep job execution away from the server that holds your repositories. Confirm which executor each project class uses, whether runners are dedicated to trust levels, and that runner and GitLab major and minor versions stay aligned.
Questions buyers ask about single-node GitLab sizing
What drives the cost of running a single-node self-managed GitLab?
Cost comes from four places: the GitLab subscription tier, the server and storage you run it on, backup storage, and the staff time to install, back up, patch and monitor it. GitLab Free has no license fee. GitLab's pricing page lists Premium at 29 US dollars per user per month billed annually, and Ultimate is custom priced. Hardware and backup storage are one-time purchases or recurring cloud charges depending on where you run. Staff time recurs every month because of the patch cadence. Ask any quote to separate the one-time setup fee from the recurring support or retainer fee, and to list which hardware or cloud resources you pay for directly.
Can our team install and run single-node GitLab without outside help?
Yes, many teams do. The Linux package bundles PostgreSQL, Redis and Sidekiq, and GitLab documents it as the most mature installation method. The harder part is the ongoing work: monthly minor releases, patch releases twice a month, and required upgrade stops that must be followed in order. Before deciding, check whether one or two people can own backups, restore tests, upgrades and monitoring. An outside partner can take the build and handover, or a retainer can cover upgrades and support. Either way, the fixed-scope agreement should name who does each task.
How long does a single-node GitLab implementation take?
Duration depends on scope: the number of users, whether SSO and SCIM are required, how many repositories need migrating, the backup target, and whether the server runs in an air-gapped network. The work usually splits into planning and access setup, installation and configuration, identity integration, backup and restore testing, and handover. Each stage waits on your approvals, network access and identity provider configuration. No duration is fixed here. DeployOpen agrees timelines in the written scope before work starts, so ask for a phased plan with the dependencies you must provide.
What do we need to provide before a GitLab build starts?
Plan to provide administrative access to the target Linux hosts or cloud accounts, a network path for users and runners, DNS and TLS certificate handling, and a decision on the identity provider. You also need a backup target with enough capacity and retention for your recovery plan, and a named owner who can approve changes. For air-gapped sites, confirm how packages and updates reach the network. Ask the scope to list each item with an owner and a due point, so delays are visible early.
When should we move from a single node to the 3K high availability design?
Move when the single server no longer meets your recovery or load needs. GitLab's reference architectures run from 1,000 users at 20 API requests per second to 50,000 users at 1,000 requests per second, and the documentation generally recommends a high availability strategy for 3,000 or more users. Other signals include frequent CI backlog, a recovery time your business cannot accept, or planned maintenance that cannot stop users. Confirm the current targets in GitLab's reference architecture documentation before sizing, and plan the HA design early if you expect growth.
What do Premium and Ultimate add beyond GitLab Free?
GitLab's pricing page lists advanced CI/CD and priority support for Premium, and application security testing, software supply chain security, vulnerability management and compliance features for Ultimate. Premium also includes Geo for warm-standby disaster recovery, according to GitLab's Geo documentation. Both paid tiers are available for self-managed installations. Choose the tier from the features your teams will use, not from user count alone, and confirm current inclusions on the pricing page before purchase.
How do backups fit into the single-node recovery plan?
A single server has no built-in failover, so the automated backup is your main recovery path. GitLab's backup and restore documentation covers the core backup task, but a complete plan also needs the secrets files, configuration and a backup target kept outside the server. Test restores on a separate host on a schedule, and record how long each restore takes so you know your real recovery window. Ask any implementation quote to include a documented restore test, not only backup job setup.
The signals that it is time for the 3K HA design
The first signal is user count approaching 3,000. GitLab's documentation generally recommends a high availability strategy at 3,000 or more users, so a single Linux package server should be reviewed well before that number is reached. Count active users, not only licensed seats, and track how that figure grows quarter over quarter. Growth is what matters for planning, because a server that is comfortable today can be undersized within one budget cycle if the organization is onboarding teams in bulk.
The second and third signals are request rate and CI load. The reference architectures scale from 1,000 users at 20 API requests per second up to 50,000 users at 1,000 requests per second, so measure your own sustained API rate from the web and API logs rather than estimating it from headcount. Watch concurrent CI jobs and monorepo size as well. Heavy pipelines and large repositories load Gitaly and Sidekiq in ways that a user count does not show, and both can push a single server past its limits even when the user total looks modest. Confirm current targets in the reference architecture documentation before you set thresholds.
The fourth and fifth signals concern recovery and upgrades. Ask whether a restore from your latest backup fits inside the outage the business will accept. If a restore takes longer than that window, or if leadership expects GitLab to stay available during upgrades, the single-node design no longer meets the requirement. The 3K HA design uses Gitaly Cluster with Praefect, PostgreSQL with Patroni, Redis Sentinel and Consul, and the 3K HA page covers the node roles for each. Plan that move early, because multi-node zero-downtime upgrades depend on that layout.
How DeployOpen scopes and delivers a single-node GitLab build
DeployOpen plans, installs, hardens, backs up and documents self-managed GitLab inside your own infrastructure, including the Linux package server that fits a single-node deployment for up to 2,000 users. We agree the scope in writing before work starts, then test the restore path before handing over.
How we scope
- Written scope names the user count, expected API load, CI concurrency and repository sizes the single node must carry.
- Scope confirms CPU, memory and storage baselines against your hosting platform and the chosen GitLab edition.
- Scope records the backup target, retention period, restore objectives and the trigger that moves you to the 3K HA design.
How we build
- Installation uses the Linux package with TLS, SMTP, outbound email and time synchronization configured in gitlab.rb.
- Hardening follows the GitLab hardening guidance for application settings, SSH keys, the operating system and configuration files, rolled out gradually.
- Automated backups are scheduled, encrypted and tested with a restore into a separate environment before go-live.
How we hand over
- Deliverables include an as-built record of versions, configuration, network paths, storage layout and secrets locations.
- Runbooks cover upgrade stops, maintenance mode, backup verification, restore steps and health checks for your operations team.
- Knowledge transfer sessions walk your administrators through daily checks, log locations and the escalation path for incidents.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer, scoped separately from the build.
- Upgrade planning follows required upgrade stops, with a rehearsal on a copy of the instance before each production step.
- Confirm with DeployOpen during scoping which support hours, response paths and runner or runner-host responsibilities apply to your team.

