Deploying self-managed GitLab on Microsoft Azure

A planned route from the GitLab Marketplace image on Azure to a production layout with managed PostgreSQL, allow-listed extensions and Blob object storage, with the decisions that set cost and risk.

On this page

Deploying self-managed GitLab on Microsoft Azure: when your team needs this layout

A production GitLab layout on Azure means your team runs GitLab inside its own subscriptions, with the application, the database, object storage and runners placed deliberately rather than all on one machine. The core decision is whether the stateful parts (PostgreSQL, Redis, Git data and job artifacts) stay on virtual machines you operate or move to managed Azure services, because that choice sets the backup, patching and upgrade work your team owns for years. GitLab's installation methods page lists the Linux package, the Helm chart, the GitLab Operator, Docker and source installs, and the Linux package is the most mature and scalable of these, which is why it is the usual starting point on Azure virtual machines.

A single virtual machine is usually enough for a small group that is still testing GitLab, has modest CI load and can tolerate a longer restore if the host fails. It stops being a good fit once you expect thousands of users or heavy pipeline traffic. GitLab's reference architectures run from 1,000 users at 20 API requests per second up to 50,000 users at 1,000 requests per second, and the documentation generally recommends a high availability strategy at 3,000 or more users. Confirm your expected user count, peak API rate and pipeline volume against the reference architectures documentation before you choose a size.

A reference-style layout on Azure separates the application from stateful services, typically with Azure Database for PostgreSQL Flexible Server, Azure Blob Storage for object data and VMs or managed services for Redis. GitLab's documentation says stateful components such as PostgreSQL and Redis are not supported inside Kubernetes, so a Cloud Native Hybrid design keeps stateful services outside the cluster. Ask your Azure team and your GitLab version which managed PostgreSQL versions and extensions are supported before committing, because those limits are set per GitLab release.

Which Azure layout fits your user count and availability needs

Azure gives you several ways to run self-managed GitLab, and the right one depends on how many users you serve and how much downtime you can accept. Each layout moves more of the operating burden onto your team, so the decision is less about features and more about who patches, backs up and scales each component.

Marketplace single VM

A single virtual machine built from the GitLab image in the Azure Marketplace runs every component on one host. It suits small teams, evaluation and proof-of-concept work, where a short outage during maintenance is acceptable. The operator owns the VM, OS patching, backups and every GitLab upgrade stop. Check that the VM size and disk match your expected repository and CI load before go-live, and confirm the image version you receive against the current upgrade path.

Linux package with external PostgreSQL and Blob storage

The Linux package installs GitLab on your own VMs, and you point it at Azure Database for PostgreSQL Flexible Server and Azure Blob Storage instead of the bundled database and local disk. This is the most common step up for growing teams because the database and object data are protected by Azure services. The operator still owns the application VMs, Redis, upgrades and the backup of GitLab secrets, and must confirm that the managed PostgreSQL version and extensions are supported by the GitLab release being installed.

Multi-node reference-style layout

Splitting Gitaly, PostgreSQL, Redis and application nodes across several VMs follows the published reference architectures, which run from 1,000 users to 50,000 users. GitLab generally recommends a high availability strategy at 3,000 or more users, using Gitaly Cluster with Praefect, PostgreSQL with Patroni, Redis Sentinel and Consul. This layout adds node count, load balancing and failover testing, so ask whether your team can operate Patroni and Praefect or whether that work belongs in a retainer.

Cloud Native Hybrid with stateful services outside Kubernetes

Cloud Native Hybrid runs stateless GitLab components on Kubernetes, such as AKS, while PostgreSQL and Redis run on VMs or managed services. GitLab does not support running stateful components like PostgreSQL and Redis inside Kubernetes, so this split is the supported way to use an AKS cluster. The operator must run the cluster, ingress and Helm upgrades in addition to the managed data services, which suits teams that already operate Kubernetes well and want Operator-driven or chart-driven upgrades.

Marketplace image, Linux package or Helm chart on Azure

The Marketplace image gives a team a prebuilt virtual machine with GitLab already installed, which shortens the first boot and suits a pilot or a small instance. The image is a packaged form of the Linux package, so the same configuration file and upgrade commands apply once the machine is running. The trade-off is that the image defines much of the starting layout, and teams often outgrow it when they need external PostgreSQL, separate Redis or object storage.

  • Fast first instance: The image removes the operating system and package setup from the first day of work.
  • Same Linux package behavior: Configuration lives in gitlab.rb and upgrades follow the Linux package process.
  • Limited layout control: Moving to an external database or object storage later means reconfiguring a running instance.
  • Image lifecycle is separate: Confirm which GitLab version the current listing ships, how often it is refreshed and whether it matches the upgrade stops you plan to follow.
  • Licensing and support terms: Confirm the Marketplace listing terms and any Microsoft charges with Microsoft and the edition details with GitLab.

Before committing, confirm the current image details with Microsoft and GitLab, including the bundled version and the supported migration path to a reference layout.

Configuration options and when each fits

This table compares the main Azure layout choices for self-managed GitLab: database, Redis, object storage, backup target and identity. Read each row against your size, availability target and the team that will operate the instance, then confirm the listed items before you commit to a design.

DecisionOption and when it fitsWhat to confirm
PostgreSQLAzure Database for PostgreSQL Flexible Server fits teams that want the provider to run backups, patching and failover for the database, and that accept the provider's admin role model.Required extensions are allow-listed for your GitLab release, the admin role GitLab needs is granted, and the server version is supported by that release.
PostgreSQLSelf-managed PostgreSQL on Azure virtual machines fits teams that need full control over extensions, replication topology or tuning, and already run database operations.Who owns patching, failover testing, point-in-time recovery drills and major version upgrades, and how that work is staffed.
Redis placementA GitLab-managed Redis on the application nodes fits small single-node instances with simple operations.Whether the reference architecture you select expects Redis Sentinel, and how Redis fails over during a node loss.
Redis placementSeparate Redis VMs fit larger or highly available layouts where cache and queue failures should not take the application tier down with them.Sentinel quorum across zones, network security group rules between Redis and Sidekiq nodes, and the Redis version pinned for your GitLab release.
Object storageAzure Blob Storage fits multi-node instances that need shared storage for artifacts, uploads, LFS, packages and backups without network file systems.Storage account and container per data type, identity used by GitLab (managed identity or access key), and whether your GitLab release supports the Azure provider settings you plan to use.
Object storageLocal disks on the GitLab host fit single-node evaluation or small pilots where data is small and recovery is simple.Disk growth over time, how artifacts and job logs are pruned, and whether a later move to Blob storage is planned and costed.
Backup targetAzure Blob Storage with immutability or retention policies fits teams that want backups kept separate from the GitLab host and the database.Retention periods set by your policy, encryption keys, restore tests on a separate host, and whether secrets files are backed up alongside application data.
Backup targetA backup volume on the GitLab host fits only short-lived test instances, because host loss also loses the backups.Whether the backup process copies off host within your recovery time objective, and who approves any exception.
Identity integrationSAML with Microsoft Entra ID fits organizations that manage workforce identity in Entra and want single sign-on with group-based access.Claim mappings, group sync behavior, enforced two-factor authentication, and a tested break-glass local administrator account.
Identity integrationLDAP fits environments with an on-premises directory that GitLab must query for users and group membership.Network path from GitLab to the directory over TLS, bind account permissions, and how users are deprovisioned when they leave.

Setup sequence from Marketplace image to production layout

This sequence takes a self-managed GitLab instance from a Marketplace image to a production layout where the application server, PostgreSQL, object storage, runners and sign-in each sit in a boundary you control. Each step closes with a check, so a failure surfaces before the next layer depends on it.

  1. Plan network and subscription boundaries

    Your cloud or platform team decides which Azure subscription, resource group and virtual network will hold GitLab, the database, the storage account and the runners. Define private endpoints or private access for PostgreSQL and Blob storage, the DNS name users will reach, and outbound paths for package mirrors and identity providers. Record these decisions in a written design that your security team approves. The step is done when every component has an agreed subnet, a name and a documented route to the others, and you can explain which traffic stays private.

  2. Provision the GitLab virtual machine

    Deploy the GitLab virtual machine from the Azure Marketplace image into the agreed subnet, then confirm the size against GitLab's server requirements for your user count rather than the smallest available size. Attach managed disks for Gitaly storage, restrict inbound ports to HTTPS and SSH from approved ranges, and set up a managed identity if you plan to use it for Blob access. The check is that the VM boots, the GitLab sign-in page responds on its private address, and the instance reports the expected GitLab version in the administrator area.

  3. Provision Flexible Server and allow-list extensions

    Create an Azure Database for PostgreSQL Flexible Server in the same region and private network as the GitLab VM, then create the GitLab database and role. Before connecting GitLab, add the PostgreSQL extensions that your GitLab release requires to the server's allow-list parameter, because they cannot be created if they are blocked. Confirm the server version against the GitLab release you are installing. The check is that a session from the GitLab VM connects over the private path, and the required extensions appear as allowed and can be created in the GitLab database.

  4. Point gitlab.rb at external PostgreSQL and Blob storage

    Edit the Linux package configuration file gitlab.rb to disable the bundled PostgreSQL, then set the external database host, port, database name and role. Configure object storage for artifacts, uploads, LFS, packages and the other data types you plan to store in Blob, using a storage account key or identity that is scoped to the GitLab containers. Run gitlab-ctl reconfigure and review any errors before continuing. The check is that gitlab-rake gitlab:check reports the database and object storage as healthy and that a test file uploaded through the UI appears in the expected container.

  5. Validate connectivity and a test backup

    Run GitLab's health checks and confirm that the application can read and write to PostgreSQL and Blob storage from the private network only. Then take a backup with the backup task, store it in a separate location, and restore it into a scratch environment to confirm the archive is usable. Note that database backups depend on tool versions matching the server, so verify the pg_dump path before relying on it. The check is a successful restore into the scratch instance, with a repository, an issue and an uploaded artifact present after restore.

  6. Enable runners with a planned executor

    Register at least one GitLab Runner in a separate network segment, choosing an executor that matches your isolation requirements and your Azure compute model. Keep the Runner major and minor versions aligned with the GitLab version, and restrict which projects can use it. Confirm that the runner can reach the GitLab URL and the package sources its jobs need. The check is a pipeline that completes on the runner, with job logs and artifacts stored in the Blob-backed object storage.

  7. Enable identity and confirm sign-in

    Connect your corporate identity provider using the authentication method your organization has approved, such as SAML or OpenID-based sign-in, and decide whether local accounts remain for break-glass use. Test sign-in with a standard user and an administrator, confirm group mapping if you use it, and disable public sign-up if your design requires it. The check is that a user from the target identity group signs in, receives the expected role, and that a user outside the group is refused.

Allow-listing PostgreSQL extensions and checking version limits

The Azure Database for PostgreSQL Flexible Server parameter that controls extension allow-listing must include every extension GitLab needs before the database is connected to the application. If an extension is missing from the allow-list, the GitLab database setup or migrations fail partway through, and the failure can look like a generic migration error rather than a platform setting. Before you create the server, compare the extensions named in the current GitLab documentation for external PostgreSQL with the allow-list you plan to apply, and record the check in your change ticket so the same list is applied in every environment, including non-production copies.

Two other items need a current check against the GitLab documentation rather than an assumption carried over from an earlier release. The first is the administrative role the Flexible Server must provide to GitLab, which determines what the Omnibus or Helm configuration can create and grant. The second is the version limit: GitLab documents which PostgreSQL major versions each GitLab release supports, and the upgrade path you choose must stay within that window. Confirm both against the GitLab upgrade paths documentation and the managed PostgreSQL sibling topic, which covers the role setup and version tables in detail. Ask your DeployOpen contact to note the PostgreSQL version and role decisions in the written scope so they are reviewed at each GitLab upgrade stop.

Backup procedure also depends on the database layer. The pg_dump client used by GitLab's backup task must be compatible with the server's major version, and a client that is older than the server can refuse to dump or produce a file that cannot be restored cleanly. Confirm the client version on the GitLab node or backup host, test a full backup and restore into a separate Flexible Server, and check that the restore includes the extensions from your allow-list. Repeat this test after every PostgreSQL major version change, because a backup that restores on the old version is not evidence that it will restore on the new one.

Security and operational risks specific to GitLab on Azure

Running self-managed GitLab on Azure puts the source code, pipeline credentials and backups of your organization inside your own subscription, so the controls around them are your responsibility. The six areas below connect network placement, secret handling, backup protection, CI/CD isolation, hardening rollout and monitoring, and each one needs an owner before the instance goes live.

Network exposure and private endpoints

Decide early which parts of the GitLab stack can be reached from the internet and which should sit on private addressing only. The PostgreSQL Flexible Server, the object storage account and the Key Vault should resolve through private connectivity from the GitLab virtual machines, not public endpoints. Check that runners, developers and the Azure services involved all reach GitLab through the paths you intended, and confirm the ingress rules against that design rather than the default network security group.

Secrets in gitlab.rb and Key Vault

The gitlab.rb file and the gitlab-secrets.json file hold keys that protect the database, CI variables and encrypted data, so losing or leaking them can break an instance or expose it. Decide which values live in the file, which are supplied from Azure Key Vault at provisioning time, and who holds access to each. Restrict file permissions on the host, keep the secrets file in your backup plan, and record where each secret is stored.

Backup encryption and retention

Backups contain repositories, the database and the secrets needed to read them, so an unencrypted or broadly readable backup exposes the whole instance. Confirm that backup archives and the object storage that holds them are encrypted, that access is limited to a restore role, and that retention periods match your policy and recovery objectives. Test a restore into an isolated environment, because a backup that has never been restored is an assumption, not a control.

Runner isolation

Runners execute code from many projects and can reach your Azure subscription if they run with broad identities or shared hosts. Separate runners by trust level, use ephemeral virtual machines where autoscaling is in use, and give each runner only the managed identity and network access its jobs need. Keep Runner versions aligned with GitLab, since the documentation advises matching major and minor versions.

Gradual hardening rollout

GitLab describes its hardening guidance as a work in progress that should be rolled out gradually, so a one-time checklist will drift from the product. Treat the guidance as a backlog: apply settings for application behavior, CI/CD, operating system and configuration files in stages, test each change against your pipelines, and record which items you accepted or deferred. Assign an owner who reviews new guidance after each upgrade.

Monitoring and alerting

Without monitoring, a failed backup job, a full data disk or an expired certificate can go unnoticed until users report it. Decide which signals are collected from the GitLab node, the PostgreSQL server and the object storage account, where logs are sent, and who receives alerts. Confirm that backup success and restore tests are reported, not only that the service responds to health checks.

Questions buyers ask before deploying GitLab on Azure

What drives the cost of deploying self-managed GitLab on Azure?

The cost has three parts: Azure resources, the GitLab subscription and the implementation work. Azure resources include virtual machines for GitLab nodes, Gitaly storage, the Azure Database for PostgreSQL Flexible Server instance, Redis, Azure Blob Storage for object data, load balancing and backup storage. These recur monthly and scale with user count, CI load and redundancy choices. The GitLab subscription depends on tier: according to the GitLab pricing page, Free costs 0 dollars, Premium costs 29 US dollars per user per month billed annually, and Ultimate is custom priced. Implementation is usually a one-time fee under a written scope. Ask any quote to separate these three lines and to state which Azure resources it assumes.

How long does a GitLab on Azure deployment take?

Duration depends on scope, mainly the number of users, whether the layout is a single instance or a multi-node reference architecture, how many existing repositories and pipelines must move, and how many identity, backup and runner integrations are included. A single-node proof of concept with default settings takes far less time than a high availability layout with managed PostgreSQL, Blob Storage object storage, a backup and restore test and a Geo secondary. The GitLab reference architecture documentation recommends a high availability strategy for 3,000 or more users, which adds components to build and test. Ask for a timeline tied to named milestones rather than a single total.

What does the work need from our own team?

Expect to provide an Azure subscription and resource group with the right permissions, network design decisions such as virtual network address space, DNS names and TLS certificates, identity provider access for single sign-on, and a named owner for approvals. You also need someone who can decide the GitLab edition, retention rules for backups and the maintenance window for upgrades. Security and compliance teams should confirm which hardening, logging and access controls they require before build starts. Agree on these inputs in the written scope so that delays do not come from waiting on decisions.

Can our team deploy GitLab on Azure without outside help?

Yes, many teams do, because GitLab documents its installation methods and supports self-managed installs on Azure infrastructure. The harder parts are the surrounding decisions: managed PostgreSQL requirements, object storage configuration, backup and restore testing, Geo design and runner fleet security. Check that your team can run upgrades through the required upgrade stops and that someone will own on-call work after go-live. If those skills are already in house, an internal build may be sensible. If they are not, a fixed-scope engagement can cover the build while your team takes over operations.

How do required upgrade stops affect the Azure layout?

GitLab requires upgrades to pass through required upgrade stops. According to the GitLab upgrade paths documentation, since GitLab 17.5 these fall at versions x.2, x.5, x.8 and x.11, for example 18.2, 18.5, 18.8, 18.11 and 19.2, 19.5, 19.8 and 19.11. On Azure, each stop affects more than the GitLab package. Check the supported PostgreSQL major version for the target release, because the managed Flexible Server must match it, and confirm the Redis and operating system versions. Plan a snapshot or backup before each stop and rehearse the path in a non-production environment. Upgrade work recurs, so ask any retainer quote how stops and testing are included.

Which Azure services need extension allow-listing or special configuration?

Azure Database for PostgreSQL Flexible Server only enables extensions that are on its allow-list, so the extensions GitLab requires must be enabled in the server parameters before the database is set up. Confirm the exact extension list for your target GitLab release in the GitLab documentation, since requirements can change between releases. Also confirm the database admin roles GitLab expects, how pg_dump compatibility affects backup tooling and whether Blob Storage access uses managed identities or access keys. Test these items on a staging server before production.

When should we choose a fixed-scope engagement instead of an open-ended one?

Choose a fixed-scope engagement when the outcome, systems and dates are clear enough to write down: the Azure layout, the GitLab edition, the identity integration, the backup and restore test and the handover documents. DeployOpen plans, implements, hardens, backs up, documents and supports self-hosted open-source platforms inside your own Azure subscriptions, and most engagements are fixed-scope with scope, deliverables and timelines agreed in writing before work starts. Ongoing support, upgrades and continuous testing are available as retainers. Pick a retainer if you expect regular upgrade stops and want the work planned in advance. Ask for a written scope that lists exclusions, so you know what is outside the price.

Keeping the Azure deployment current through upgrade stops

GitLab requires every upgrade to pass through the required upgrade stops, so an Azure deployment cannot simply jump to the newest release. Since GitLab 17.5, those stops fall at versions x.2, x.5, x.8 and x.11, with examples in the supplied upgrade path documentation including 18.2, 18.5, 18.8, 18.11, 19.2, 19.5, 19.8 and 19.11. Plan your path from your current version to the next stop, then to the stop after that, and confirm each step against the GitLab upgrade paths documentation before scheduling a window. The maintenance policy also shapes timing: a major release arrives yearly, minor releases ship on the third Thursday of each month, and patch releases twice a month. Bug fixes are backported only to the current stable release, while security fixes also reach the previous two monthly releases, so an instance that trails by several months may lose fixes it expected to receive.

Before each stop, take a fresh backup and, on Azure, a snapshot of the virtual machine disks that hold configuration and Gitaly data. The GitLab backup task covers repositories, the database and uploads, but it does not replace a platform-level restore point, and /etc/gitlab/gitlab-secrets.json and gitlab.rb must be preserved separately. Confirm the exact backup procedure for your version in the GitLab backup and restore documentation and test a restore on a non-production copy at least once.

The upgrade plan must include the parts of the stack that sit outside the GitLab package. Azure Database for PostgreSQL Flexible Server has its own maintenance windows and major version limits, and each GitLab release restricts which PostgreSQL versions it supports, so check that pairing before the stop and confirm the extension allow-list still matches what the new release expects. Azure Blob storage holds artifacts, LFS objects and other uploads, so confirm the object storage configuration, the storage account access keys or identities, and the retention rules still work after the upgrade. Record the database and storage state alongside the GitLab snapshot so a rollback restores all three to the same point.

How DeployOpen works on this

DeployOpen plans, implements, hardens, backs up, documents and supports self-hosted GitLab inside your own Azure subscriptions and networks. For an Azure deployment, we start from a written scope that sets the Marketplace image, the Azure Database for PostgreSQL Flexible Server layout, extension allow-listing and Azure Blob object storage before any build begins.

How we scope

  • Written scope names the GitLab version, edition, user count and Azure subscription, region and networking boundaries.
  • Scope confirms which Azure Database for PostgreSQL Flexible Server settings and extensions GitLab needs for your release.
  • Scope lists which Runner fleets, identity integrations and object storage buckets are in or out of the engagement.

How we build

  • We build the Azure layout as code or documented steps, so you can rebuild the environment and review every setting.
  • We configure the Linux package or chosen install method against the managed database and Azure Blob storage, then test each connection.
  • We test backup, restore and a required upgrade stop in a non-production copy before any production cutover.

How we hand over

  • You receive architecture diagrams, configuration files and access records for the Azure resources and the GitLab instance.
  • Runbooks cover backups, restores, maintenance mode, health checks and the upgrade path your release requires.
  • We walk your platform and security teams through the handover, and document open items with owners and dates agreed in writing.

How we support

  • Ongoing support, upgrades and continuous testing are available as retainers after the fixed-scope build ends.
  • Upgrade planning follows the required upgrade stops in GitLab's documentation, with each step tested before production.
  • Confirm with us during scoping how support responsibilities split between your team, Microsoft and DeployOpen.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.