Running GitLab in Docker for production: when the image is a sound choice
Docker is a supported way to install self-managed GitLab, but whether it fits your production estate depends on your operating model and how much support you expect to rely on. Infrastructure teams weighing the GitLab Docker image against the Linux package should start by asking who will patch, back up and troubleshoot the instance, and which installation method GitLab documents as the default path for that work. GitLab's installation methods page lists five options: the Linux package, the Helm chart, the GitLab Operator, Docker and installation from source.
The Linux package is the method GitLab describes as the most mature and scalable. It bundles PostgreSQL, Redis and Sidekiq with the application, so the components are versioned and configured together through /etc/gitlab/gitlab.rb. The Helm chart targets Kubernetes, and the Operator targets OpenShift and similar platforms. Source installation gives the most control but leaves the most work with your team. Docker sits between the package and the chart: it packages the application in a container, but the stateful services, storage layout and upgrade sequence still need to be designed by you.
Before choosing Docker, confirm three things with GitLab documentation and your own platform team: the container image tags you plan to pin for each upgrade stop, how your reference architecture maps onto containers, and whether your support entitlement covers the Docker deployment. Make the decision in writing so the trade-off is visible to the people who will carry the pager.
Deciding whether Docker fits your production GitLab
Running GitLab in a container is a sound choice only when several earlier decisions line up. The cards below cover the platform, team skills, deployment model, scale, the bundled services you take on, and how support is handled. Work through them in order, because the answer to one often settles the next.
Existing container platform
Ask whether your organization already runs and patches a container platform with image registries, monitoring and backup routines. If it does, a GitLab image fits your existing release process. If containers are new to your operations team, the GitLab Linux package on virtual machines may be easier to run and recover, and the installation methods page lists the alternatives you should compare against.
Team fluency with Docker and Compose
Confirm who will edit the Compose file, mount volumes, read container logs and run docker exec during an incident. Upgrades, configuration changes through gitlab.rb and restores all depend on those skills. Name at least two people who can perform a restore from a backup without outside help before you commit to this method.
Consistent image-based delivery
Choose Docker when you want each environment to run the same pinned image tag, promoted through staging before production. Image-based rollback is straightforward only if your persistent data lives in volumes you have mapped and backed up. Write down the tag pinning rule and the approval step for each upgrade before the first install.
Single-node versus multi-node scale
A single container host suits small user counts, but GitLab's reference architectures begin at 1,000 users and generally recommend a high availability strategy from 3,000 users. Above those thresholds, planning for Gitaly, PostgreSQL, Redis and Sidekiq as separate components matters more than the container format. Check the reference architecture that matches your user and API load before you size the host.
Appetite for bundled services
The Docker image runs PostgreSQL, Redis and other services inside one container unless you point GitLab at external services. Decide whether your team wants to operate those components or prefer managed databases and separate object storage. Treat the external service choice as a design decision with its own backup, patching and failover owners.
Support path and vendor terms
GitLab notes a support trade-off for Docker and Compose deployments, so ask GitLab directly which support scope applies to your chosen method and edition before you rely on it. Clarify who you call for a stuck upgrade, which logs and diagnostics you must collect, and whether your subscription tier includes priority support. Record the answer in your runbook.
Which GitLab installation method fits which team
Docker and Compose suit a single virtual machine where your team already runs containers and wants a declarative file that defines the GitLab container and its settings. The image packages the GitLab application, but the database, cache and object storage decisions remain yours, along with the host they run on.
- Bundled services: the image includes GitLab's application stack, but you decide where PostgreSQL, Redis and object storage run and who patches them.
- Upgrades: you change the image tag, restart the container and keep the same volumes, and you still pass through every required upgrade stop.
- Volume layout: configuration, data and logs live in mounted host paths, so your backup plan starts with that layout.
- Documented scope: GitLab lists Docker as an installation method on its installation methods page. Confirm with GitLab the support scope for Docker and Compose deployments before you commit.
Before choosing this path, check whether your team can own host patching, volume backups and restore tests on a schedule.
The Linux package is the installation method GitLab describes as the most mature and scalable. It bundles PostgreSQL, Redis and Sidekiq, so a single virtual machine can run a complete instance with settings managed in one configuration file. It is the common base for teams that expect to grow into multi-node layouts.
- Bundled services: PostgreSQL, Redis and Sidekiq ship inside the package, and your team maintains them through GitLab's configuration and upgrade process.
- Upgrades: the package is upgraded through the operating system package manager, and each upgrade must follow the required upgrade stops.
- Growth path: the same installation method underpins the larger reference architectures, where external or separate components take on more of the load.
- Operating burden: host operating system patching, certificate renewal and backup scheduling stay with your team.
Confirm with GitLab which operating systems and package versions are supported for your target release, and check the reference architecture guidance before you size the server.
The Helm chart fits teams that already operate a Kubernetes platform and want GitLab deployed through the same release tooling as other workloads. GitLab's reference architecture documentation states that stateful components such as PostgreSQL and Redis should not run inside Kubernetes. The Cloud Native Hybrid architecture runs stateless components on Kubernetes and places stateful services on virtual machines or managed services.
- Bundled services: plan to supply PostgreSQL, Redis and object storage from outside the cluster rather than relying on in-cluster databases.
- Upgrades: chart versions are rolled out through your release process, and each GitLab upgrade still follows the required upgrade stops.
- Platform ownership: cluster upgrades, ingress, storage classes and node pools become dependencies that GitLab upgrades must fit.
- Documented scope: check GitLab's reference architecture and installation pages for the chart version you plan to use.
Before committing, confirm with GitLab which external services it supports with your chosen chart and architecture, and who in your organization owns the stateful services.
The GitLab Operator is a separate installation method listed by GitLab, and it fits teams running OpenShift that want the cluster's operator model to manage GitLab components. OpenShift adds platform rules, such as security context constraints, that affect how GitLab pods can run. The Operator choice should follow the platform team's standards, not the other way around.
- Bundled services: confirm which stateful services the Operator deploys and which you must provide externally, and plan them against the reference architecture guidance.
- Upgrades: the Operator drives component changes, but GitLab's required upgrade stops still apply to the version you move between.
- Platform fit: identify the security context constraints and storage requirements your OpenShift administrators must approve before installation.
- Documented scope: GitLab's installation methods page lists the Operator. Confirm with GitLab the OpenShift versions and features it supports for your release.
Before installation, ask GitLab in writing which OpenShift configuration it has validated for your target version and which parts of the setup remain your responsibility.
Offline and air-gapped environments are a deployment condition rather than a separate installation method. GitLab documents running self-managed instances in these networks, with separate guidance for its security scanners and for GitLab Duo Self-Hosted. The method you choose must work with a network that cannot pull packages, images or updates from the internet during operation.
- Artifact transfer: every package, container image and dependency needs an approved internal source, so plan a transfer process for each release.
- Upgrades: each upgrade needs the new artifacts, and the required upgrade stops still apply, so plan extra lead time for transfers.
- Bundled services: the method you pick determines which databases, caches and storage you must mirror or provide internally.
- Feature availability: scanners and AI features may need their own offline steps, so check each one against the offline guidance.
Ask GitLab which installation methods its offline guidance covers for your target release and version, and confirm how it documents updates for your network.
Docker image configuration choices and when each fits
This table compares the main configuration decisions for the GitLab Docker image: how settings are passed in, whether PostgreSQL and Redis are bundled or external, where artifacts and uploads live, and how HTTPS is terminated. Read each row for the condition under which the option fits, the trade-off it introduces, and the items to confirm against the version you plan to run. The main takeaway is that every external service you adopt moves work and responsibility to your team.
| Decision | When it fits | Trade-off | What to confirm |
|---|---|---|---|
| Settings via GITLAB_OMNIBUS_CONFIG | Small, self-contained settings set in the container environment and kept in your deployment definition | Long configuration becomes hard to review and diff, and the variable must be kept in sync across container recreations | Confirm the variable syntax for your target GitLab version and that secrets are not stored in plain text in version control |
| Mounted gitlab.rb file | Larger configurations that your team reviews, versions and changes through a standard process | You must manage the file on the host and ensure the container reads it on each start | Confirm the mount path in the official Docker documentation and the reconfigure step needed after edits |
| Bundled PostgreSQL and Redis | Single-node evaluation or small installations where the image manages its own databases | Backups, upgrades and recovery of the databases stay inside the container and your backup tasks | Confirm the bundled PostgreSQL and Redis versions for your release and your backup approach for each |
| External PostgreSQL and Redis | Teams that already operate database services with backup, monitoring and patching standards | You own connectivity, credentials, sizing and version compatibility with each GitLab release | Confirm supported PostgreSQL and Redis versions per release and any required extensions on your database service |
| Local volumes for artifacts and uploads | Small instances or short-lived test environments where local disk is sufficient | Data grows on the host, and backup and restore depend on volume handling and disk space | Confirm your backup scope covers the data directories and the disk growth rate for your CI and project load |
| External object storage for artifacts and uploads | Instances with growing CI artifacts, package or registry data, or a need for storage separation and backup | Requires a storage endpoint, credentials and per-data-type settings that must be tested | Confirm which data types your version stores in object storage and whether your provider or Ceph endpoint is supported |
| TLS terminated inside the GitLab container | Deployments where the container must serve HTTPS directly with certificates you manage | Certificate renewal and cipher configuration are handled in the container and must be repeated on upgrades | Confirm the certificate paths and renewal process in the official Docker documentation for your version |
| TLS terminated at a reverse proxy or load balancer | Environments with an existing ingress layer, central certificate management or several services behind one entry point | The proxy must forward headers and ports correctly, and misconfiguration can cause redirect loops or wrong URLs | Confirm the external URL, forwarded headers and proxy timeouts against the GitLab reverse proxy guidance for your version |
How to lay out volumes for the GitLab container
This sequence maps the container's configuration, data and log paths to host directories so that GitLab state survives a container recreate, can be backed up per volume, and can be restored with correct ownership. It produces a documented volume map, a verified permissions baseline and a tested recreate that leaves repositories, database state and configuration intact.
Confirm the container paths for your image
Before mapping anything, the platform engineer checks the current GitLab Docker image documentation for the container paths used for configuration, logs and data. Those paths are the ones your host directories must mount to, and they can change between releases, so the check should be repeated for the exact image tag you plan to run. Record the paths, the image tag and the GitLab version in the change record. The step is done when the three path groups are written down and matched to that tag, with the source page noted.
Create separate host directories for each volume
The platform engineer creates one host directory for configuration, one for data and one for logs, on a filesystem with local storage that meets the hardware baseline for your user count. Keep them as separate paths under a single parent so each can be sized, snapshotted and backed up independently. Do not place these directories on network file shares that GitLab does not support for its data. The step is done when each directory exists, is on the intended disk, and shows its mount point and free space in the change record.
Set ownership and permissions before the first start
The platform engineer sets the owner, group and mode of each host directory to match the user the container runs as, which you confirm in the image documentation rather than assume. Permissions set after a first start can leave files owned by the wrong user, and the database and Git data are the most sensitive to this. Restrict the configuration directory so only the operators who maintain it can read it. The step is done when a test file written from inside a throwaway container lands with the expected owner, and the directory modes are recorded.
Map volumes in Compose or the run command
The platform engineer writes the volume mappings in the Compose file or run command, pairing each host directory with the container path confirmed in the first step. Keep the mappings in version control with the rest of the deployment definition, and avoid anonymous volumes for any state. Store the GitLab root and secrets material in the configuration volume, not in the image. The step is done when the rendered configuration lists each host-to-container pair, and a dry review confirms no state path is left unmapped.
Back up each volume and the host-level secrets
The platform engineer takes a host-level snapshot or copy of the configuration volume, including the secrets files, before the first production start and before every upgrade. The GitLab backup task covers repositories and the database, but it does not replace host-level copies of configuration secrets, which GitLab needs to decrypt stored data. Store these copies encrypted, in a location separate from the host. The step is done when a copy of each volume exists, is readable on a restore host, and its checksum is recorded.
Test that data survives a container recreate
The platform engineer stops the container, removes it, and starts a new one from the same image tag and volume map, then checks that projects, users and settings are still present. This test proves the state lives on the host and not inside the container layer. Run it on a non-production copy first, and confirm the GitLab health checks report healthy once the start completes. The step is done when repositories, a test pipeline and a user login work after the recreate, and the result is recorded.
Document the layout and schedule recurring checks
The platform engineer writes the volume map, owner and mode settings, backup locations and recreate test steps into the runbook the operations team uses. Include the exact image tag and the source page for the container paths, so the next upgrade starts from confirmed facts. Ask your provider or internal team to set a recurring check that compares the mounted paths and ownership against this record. The step is done when the runbook is reviewed by a second operator and the recurring check is in place.
Planning Docker upgrades through required upgrade stops
This sequence moves a production GitLab Docker deployment from its current version to a target release one required upgrade stop at a time, with a backup and a health check at every step so any failed stop can be rolled back.
Confirm the supported release window
Before choosing a target, check the GitLab maintenance policy to see which releases currently receive fixes. Bug fixes are backported only to the current stable release, and security fixes are also backported to the previous two monthly releases. Record the current version of your instance, the releases still in the support window, and the date you last reviewed the policy. Your team owns this step. The output is a written note that names your starting version and the window you intend to stay within.
Identify the next required upgrade stop
Use the GitLab upgrade paths documentation to find the next required stop above your current version. Since GitLab 17.5, stops fall at versions x.2, x.5, x.8 and x.11. For example, an instance on 18.2 moves to 18.5 next, then 18.8, then 18.11, and then 19.2, 19.5, 19.8 and 19.11. Do not skip a stop, even when the final target is far away. The output is an ordered list of stops from your current version to the target, which becomes the runbook for the upgrade.
Take a full backup before each stop
Run a complete GitLab backup, including the database, repositories and uploads, and copy the backup and the configuration files, such as the gitlab.rb file and the secrets file, to storage outside the container host. Store the secrets file with the backup, because restoring without it can leave encrypted data unreadable. Confirm the backup file is complete and restorable by checking its contents and recording the checksum. The output is a backup set tied to the version being left behind, which is the rollback point for that stop.
Pin the exact image tag for the stop
Edit the image reference in your Compose file or container definition so it names the exact version of the next stop, such as a specific tag for 18.5. Do not use latest, because it changes without notice and makes it unclear which version is running. Keep the Compose file and environment settings in version control so the previous tag can be restored quickly. The output is a committed change that records the stop version and the reason for it.
Apply the upgrade to the stop version
Stop the running container, pull the pinned image and start the stack with the new tag. Watch the container logs during startup so database migrations and reconfiguration steps finish without errors before you continue. Allow the instance to complete background migrations before moving on, as the upgrade documentation describes. The output is a running container reporting the stop version, with no failed migration or startup errors in the logs.
Verify health before the next stop
Check that the web interface loads, that users can sign in, that a test repository can be cloned and pushed, and that a test pipeline runs on a registered runner. Review the health and readiness checks that GitLab exposes and confirm the version shown in the Admin Area matches the pinned tag. Record the results in the change log. The output is a signed-off checklist for the stop. If any check fails, restore from the backup taken for that stop and the previous image tag before investigating.
Repeat the cycle until the target is reached
Repeat the backup, pin, upgrade and verification steps for each remaining stop in the runbook until the target version is running. Keep the window from the first step in mind, so that each stop still falls within the supported releases your policy requires. Confirm that the team has a rollback plan for each stop and that a fresh backup exists before the final stop. The output is a running instance at the target version with a recorded history of every stop and its checks.
Questions teams ask before running GitLab in Docker in production
Does GitLab support running self-managed GitLab in Docker in production?
Yes, Docker is one of the documented installation methods for self-managed GitLab, alongside the Linux package, the Helm chart, the GitLab Operator and source installs. GitLab's installation methods page describes the Linux package as the most mature and scalable option, so the Docker choice should rest on a clear reason, such as an existing container platform and image-based rollouts. Before you commit, read the Docker guidance on GitLab's installation methods page and confirm which deployment shapes and user counts it covers for your organization.
What does GitLab's support trade-off for Docker mean in practice?
The trade-off is that the terms of support for Docker and Compose deployments can differ from those for the Linux package, so you need to confirm them in writing before go-live. The exact terms are not something to assume from the installation page, and they should be checked against your subscription. If you hold Premium, which lists priority support, ask GitLab whether that support applies in the same way to a Docker deployment. Record the answer in your change management file, along with the version you are running and the diagnostic information support will ask for.
How much effort do Docker setup and upgrades take, and what drives it?
Effort depends mainly on how many upgrade stops lie between your current version and your target. GitLab requires upgrades to pass through required stops, which since GitLab 17.5 fall at x.2, x.5, x.8 and x.11, so a 19.5 instance cannot jump directly to 19.8 without first checking the path. Other drivers include how often you apply the monthly minor and twice-monthly patch releases, how cleanly your image tags and volumes are defined, whether backups are verified before each step, and how much testing happens in a staging copy. Ask any provider to itemize setup, upgrade rehearsal and handover separately.
Can our team run GitLab in Docker without outside help?
A team can run it in-house if it already operates Linux servers and containers well, manages TLS and DNS, can restore from backup in a test environment, and has an owner for the GitLab Runner fleet. Gaps usually appear in upgrade discipline, monitoring, and recovery testing, not in the first install. Check the hardening guidance, which GitLab describes as a work in progress to roll out gradually, and assess whether your team can keep pace with the maintenance policy. Many organizations split the work, keeping day-to-day operation in-house while a partner plans, builds or reviews specific parts. DeployOpen works on fixed-scope engagements agreed in writing, and it also offers retainers for ongoing support and upgrades.
What does a Docker deployment need from us for storage, backups and runners?
You need a container host or platform with persistent storage for the configuration, logs and data directories, a storage layout you can back up and restore, and a plan for the GitLab Runner fleet that executes CI/CD jobs. Confirm the backup and restore procedure against GitLab's backup and restore documentation and test a restore before you rely on it. For runners, GitLab Runner is MIT licensed, supports shell, Docker and autoscaling executors, and GitLab advises keeping Runner major and minor versions aligned with GitLab. Decide early whether runners run on the same host, separate VMs or a cloud autoscaling pool.
How does a Premium or Ultimate subscription relate to choosing Docker?
The installation method does not change the subscription price. GitLab Free costs 0 US dollars, Premium costs 29 US dollars per user per month billed annually, and Ultimate is custom priced, and all three tiers are available for self-managed installations, so the choice of Docker or Linux package is separate from the edition decision. Premium adds advanced CI/CD and priority support, while Ultimate adds application security testing, software supply chain security, vulnerability management and compliance features. Your recurring costs will also include infrastructure, runner compute and the people who operate the instance. Ask any quote to separate one-time setup from ongoing support, upgrades and runner costs, and check current pricing on GitLab's pricing page.
Security and operations to plan for a containerized GitLab
A containerized GitLab keeps the same security and recovery obligations as a Linux package install, but the host, the image pull path and the mounted configuration volume become part of your control set. The cards below cover the items to plan before go-live and the checks to repeat after each upgrade.
Apply hardening through gitlab.rb
Most of GitLab's hardening settings live in the configuration file mounted into the container, so the file itself is a controlled artifact. Review the hardening guidance against your own policy, then change settings in small batches so each change can be tested and rolled back. Treat the hardening guidance as a work in progress that you apply gradually, not a checklist to finish once. Record which settings you changed and why.
Secure the host and image pull path
Decide who can pull images, from which registry, and how image digests are verified before a container is replaced. Pin the image tag or digest used in production so an upgrade is a deliberate change rather than a silent pull. Confirm the host operating system is patched on your normal cycle and that only administrators can run Docker commands on it, since Docker access is effectively root access.
Protect secrets and the config volume
The configuration and secrets directories hold the keys that decrypt database fields and signing material, so losing them breaks restores even when data backups succeed. Restrict file permissions on the host directories, keep copies in a secrets manager you control, and make sure those copies are included in your backup scope. Confirm where each secret is stored and who can read it.
Isolate runners and match versions
Runners execute untrusted pipeline code, so run them separately from the GitLab container on hosts or autoscaled machines that do not hold production credentials. Keep the Runner major and minor versions aligned with the GitLab version you run, and plan Runner upgrades in the same change window as GitLab upgrades. Check the executor type and what network access jobs receive.
Monitor health and watch container state
Container restarts can hide a failing database, a full volume or a background job backlog, so monitoring should cover the container, the disk under the data and logs volumes, and GitLab's own health endpoints. Confirm that alerts reach an on-call team and that someone reviews them. Check that the logs volume rotates so a busy instance does not fill the host.
Test restores before upgrades
A backup that has never been restored is an assumption. Schedule restore tests into a separate environment that uses the same image version and the same secrets, then verify that repositories, CI variables and attachments open correctly. Run a restore test before every required upgrade stop, so you know the rollback path works before you need it.
How DeployOpen scopes a Docker-based GitLab deployment
DeployOpen scopes a Docker-based GitLab deployment as a fixed-scope engagement, with scope, deliverables and timeline agreed in writing before work starts. The first decision is the install method. GitLab documents Docker alongside the Linux package, the Helm chart, the GitLab Operator and source installs, so the scope records why a container fits your platform team's skills, patching habits and recovery plans. We confirm the image tag policy, the host layout, and the network path to the database and object storage. We also list which GitLab features in use depend on the container setup, so each one is checked during the build instead of discovered after go-live. Each item becomes a line in the statement of work.
Volume layout and backups are where most of the scoped effort sits. The scope maps the configuration, log and data directories to dedicated volumes, sets their sizing and snapshot schedule, and defines how gitlab.rb and the secrets file are backed up alongside the repository data. Restores are tested on a separate host, and the scope names who signs off on that test. The upgrade calendar is planned from GitLab's maintenance policy, which ships patch releases twice a month and a minor release on the third Thursday of each month, and from the required upgrade stops listed in the GitLab upgrade paths documentation. We agree the target version, the stops the instance must pass through, and who approves each change window.
The runner plan covers where CI/CD jobs execute, how Runner versions stay aligned with the GitLab major and minor version, and which executor keeps jobs isolated from the instance host. Verification closes the scope: an upgrade rehearsal on a copy of the instance, a restore test, and a checklist of health checks and monitoring. DeployOpen does not publish a price list. A quote follows from the written scope, so the inputs that move cost are the number of instances, the volume and backup targets, the size of the runner fleet, and the number of upgrade windows in the first year. Ongoing upgrades and support after handover can be covered by a retainer, which is priced and scoped separately from the implementation.
How DeployOpen delivers and supports self-managed GitLab
DeployOpen plans, implements, hardens, documents and supports self-hosted open-source platforms inside your own infrastructure. For Running GitLab in Docker for production, we start with a written scope that decides whether a container-based layout fits your size, support position and upgrade rhythm before any build begins.
How we scope
- Confirm whether the Docker image fits your user count, CI load and recovery targets before choosing an install method
- Document the volume layout, host sizing, network placement and external services each container depends on
- Agree the upgrade stops, backup approach and support position in writing before work starts, as engagements are fixed-scope
- Ask for a quote that itemizes scoping, build, testing and handover so cost components are visible
How we build
- Deploy the GitLab Docker image on infrastructure you control with persistent volumes for config, logs and data
- Configure gitlab.rb settings, backups and object storage paths so state survives container replacement
- Test a full upgrade from one required upgrade stop to the next in a staging copy before touching production
- Validate backup and restore with a test restore, recording what was restored and how long it took
How we hand over
- Provide runbooks covering the volume layout, container restart behavior, upgrade procedure and rollback steps
- Document the container image tag policy and the process for checking GitLab's published upgrade paths
- Walk your platform and security teams through monitoring, health checks and the support trade-offs GitLab notes for Docker and Compose
- Hand over configuration as code so your team can rebuild the instance from recorded inputs
How we support
- Offer ongoing support, upgrades and continuous testing as a retainer if your team wants DeployOpen to keep operating the instance
- Plan each GitLab patch and minor release against the maintenance policy and schedule upgrades around required stops
- Confirm with GitLab which support level applies to your Docker deployment, since support terms vary by tier
- Review backups, restore tests and volume growth on a recurring schedule agreed in the written scope

