---
# === IDENTITY ===
id: software/devops/docker-gitlab/2026
canonical_question: "Docker Compose reference: GitLab CE"
aliases:
  - "GitLab CE Docker Compose setup"
  - "self-hosted GitLab Docker installation"
  - "docker-compose.yml for GitLab Community Edition"
  - "GitLab omnibus Docker container configuration"
  - "GitLab Runner Docker Compose"
  - "GitLab CE backup restore Docker"
  - "GitLab Docker SMTP email configuration"
  - "deploy GitLab CE with Docker Compose"
entity_type: software_reference
domain: software > devops > Docker GitLab CE
region: global
jurisdiction: global
temporal_scope: 2025-2026

# === VERIFICATION ===
last_verified: 2026-02-27
confidence: 0.93
version: 1.0
first_published: 2026-02-27

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "GitLab 17.0 requires PostgreSQL 14+; GitLab 18.0 (2025) introduced new UI and Duo features"
  next_review: 2026-08-26
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Docker for Windows is NOT supported -- use Docker on Linux or macOS for production"
  - "Minimum 4 GB RAM + 1 GB swap for GitLab CE; 8 GB RAM recommended for production (up to 500 users)"
  - "NEVER expose GitLab without TLS in production -- always set external_url to https:// and configure SSL certificates"
  - "Backup command does NOT back up gitlab-secrets.json -- you MUST back up /etc/gitlab/gitlab-secrets.json separately"
  - "Runner registration tokens are deprecated since GitLab 16.0 -- use runner authentication tokens from the UI instead"
  - "GITLAB_OMNIBUS_CONFIG settings are NOT written to gitlab.rb -- they are evaluated on container start only"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Deploying GitLab on Kubernetes"
    use_instead: "Use the official GitLab Helm Chart or GitLab Operator instead of Docker Compose"
  - condition: "Using GitLab.com SaaS (not self-hosted)"
    use_instead: "No Docker setup needed -- use gitlab.com directly"
  - condition: "Need GitLab EE (Enterprise Edition) features"
    use_instead: "Replace gitlab/gitlab-ce with gitlab/gitlab-ee in the compose file"

# === AGENT HINTS ===
inputs_needed:
  - key: "deployment_type"
    question: "Is this for local development or production?"
    type: choice
    options: ["local development", "production server"]
  - key: "ssl_setup"
    question: "Do you need HTTPS/SSL? If so, do you have your own certificates or want Let's Encrypt?"
    type: choice
    options: ["no SSL (HTTP only)", "Let's Encrypt auto", "own certificates", "reverse proxy handles SSL"]
  - key: "ci_runner"
    question: "Do you need a GitLab Runner for CI/CD pipelines?"
    type: choice
    options: ["yes", "no"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/devops/docker-gitlab/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-02-27)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "software/devops/docker-compose-basics/2026"
      label: "Docker Compose Fundamentals"
  solves: []
  alternative_to:
    - id: "software/devops/gitea-docker/2026"
      label: "Gitea Docker Compose Setup"
  often_confused_with:
    - id: "software/devops/gitlab-ci-pipelines/2026"
      label: "GitLab CI/CD Pipeline Configuration"

# === SOURCES (7 authoritative sources) ===
sources:
  - id: src1
    title: "Install GitLab in a Docker container"
    author: GitLab Inc.
    url: https://docs.gitlab.com/install/docker/installation/
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src2
    title: "Configure GitLab running in a Docker container"
    author: GitLab Inc.
    url: https://docs.gitlab.com/install/docker/configuration/
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src3
    title: "Back up GitLab running in a Docker container"
    author: GitLab Inc.
    url: https://docs.gitlab.com/install/docker/backup/
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src4
    title: "Run GitLab Runner in a container"
    author: GitLab Inc.
    url: https://docs.gitlab.com/runner/install/docker/
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src5
    title: "SMTP settings"
    author: GitLab Inc.
    url: https://docs.gitlab.com/omnibus/settings/smtp/
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src6
    title: "Running GitLab in a memory-constrained environment"
    author: GitLab Inc.
    url: https://docs.gitlab.com/omnibus/settings/memory_constrained_envs/
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src7
    title: "Registering runners"
    author: GitLab Inc.
    url: https://docs.gitlab.com/runner/register/
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
---

# Docker Compose Reference: GitLab CE

## TL;DR

- **Bottom line**: GitLab CE runs as a single omnibus Docker container (`gitlab/gitlab-ce`) with three volume mounts for config, logs, and data, plus optional Runner and SMTP containers for CI/CD and email.
- **Key tool/command**: `docker compose up -d` with a `docker-compose.yml` that maps ports 80/443/22 and mounts `$GITLAB_HOME/{config,logs,data}`.
- **Watch out for**: The initial root password auto-deletes after 24 hours -- retrieve it immediately with `docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password`.
- **Works with**: GitLab CE 18.x (latest), Docker Engine 24+, Docker Compose V2. Linux hosts recommended; Docker for Windows is NOT supported.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Docker for Windows is NOT supported -- use Docker on Linux or macOS for production
- Minimum 4 GB RAM + 1 GB swap for GitLab CE; 8 GB RAM recommended for production (up to 500 users)
- NEVER expose GitLab without TLS in production -- always set external_url to https:// and configure SSL certificates
- Backup command does NOT back up gitlab-secrets.json -- you MUST back up /etc/gitlab/gitlab-secrets.json separately
- Runner registration tokens are deprecated since GitLab 16.0 -- use runner authentication tokens from the UI instead
- GITLAB_OMNIBUS_CONFIG settings are NOT written to gitlab.rb -- they are evaluated on container start only

## Quick Reference

| Service | Image | Ports | Volumes | Key Env |
|---|---|---|---|---|
| GitLab CE | `gitlab/gitlab-ce:18.7.1-ce.0` | `80:80`, `443:443`, `22:22` | `config:/etc/gitlab`, `logs:/var/log/gitlab`, `data:/var/opt/gitlab` | `GITLAB_OMNIBUS_CONFIG`, `external_url` |
| GitLab Runner | `gitlab/gitlab-runner:latest` | None | `runner-config:/etc/gitlab-runner`, `/var/run/docker.sock:/var/run/docker.sock` | `CI_SERVER_URL`, `REGISTRATION_TOKEN` (deprecated) |
| PostgreSQL (bundled) | Included in omnibus | 5432 (internal) | Stored in `data:/var/opt/gitlab/postgresql` | `postgresql['shared_buffers']` |
| Redis (bundled) | Included in omnibus | 6379 (internal) | Stored in `data:/var/opt/gitlab/redis` | `redis['maxmemory']` |
| Nginx (bundled) | Included in omnibus | 80, 443 (via GitLab) | SSL certs in `config:/etc/gitlab/ssl` | `nginx['ssl_certificate']` |
| Puma (app server) | Included in omnibus | 8080 (internal) | -- | `puma['worker_processes']` (default: 2) |
| Sidekiq (background) | Included in omnibus | -- | -- | `sidekiq['max_concurrency']` (default: 20) |
| Prometheus (monitoring) | Included in omnibus | 9090 (internal) | Stored in `data:/var/opt/gitlab/prometheus` | `prometheus['enable']` |
| Container Registry | Included in omnibus | 5050 (configurable) | `data:/var/opt/gitlab/gitlab-rails/shared/registry` | `registry_external_url`, `registry['enable']` |
| Gitaly (Git RPC) | Included in omnibus | 8075 (internal) | Stored in `data:/var/opt/gitlab/git-data` | `gitaly['configuration']` |

**Volume Mapping:**

| Host Path | Container Path | Purpose |
|---|---|---|
| `$GITLAB_HOME/config` | `/etc/gitlab` | gitlab.rb, gitlab-secrets.json, SSL certs |
| `$GITLAB_HOME/logs` | `/var/log/gitlab` | Omnibus component logs |
| `$GITLAB_HOME/data` | `/var/opt/gitlab` | Repositories, uploads, PostgreSQL data, Redis data |

**Memory Allocation (production):**

| Component | Default Memory | Tunable Setting |
|---|---|---|
| Puma (per worker) | 1.2 GB limit | `puma['per_worker_max_memory_mb']` |
| Sidekiq | ~200 MB initial, grows | `sidekiq['max_concurrency']` |
| Prometheus + exporters | ~200 MB | `prometheus['enable'] = false` to disable |
| PostgreSQL | 256 MB shared_buffers | `postgresql['shared_buffers'] = '512MB'` |
| Total minimum | ~4 GB | -- |

## Decision Tree

```
START: What kind of GitLab Docker deployment?
|-- Local development / testing?
|   |-- YES --> Use HTTP-only compose (Step 1) with minimal resources
|   +-- NO v
|-- Production with own domain?
|   |-- YES --> Do you handle SSL at a reverse proxy (Nginx, Traefik, Caddy)?
|   |   |-- YES --> Set external_url to https:// but disable built-in Nginx SSL (Step 3, Variant B)
|   |   +-- NO --> Use Let's Encrypt auto-SSL (Step 3, Variant A)
|   +-- NO v
|-- Need CI/CD pipelines?
|   |-- YES --> Add GitLab Runner service to compose (Step 4) and register it
|   +-- NO v
|-- Need email notifications?
|   |-- YES --> Configure SMTP in GITLAB_OMNIBUS_CONFIG (Step 5)
|   +-- NO v
+-- DEFAULT --> Basic compose with HTTP, add features as needed
```

## Step-by-Step Guide

### 1. Set up the host environment

Create the GitLab home directory and set the environment variable. [src1]

```bash
# Set GITLAB_HOME (add to ~/.bashrc or ~/.profile for persistence)
export GITLAB_HOME=/srv/gitlab

# Create directories
sudo mkdir -p $GITLAB_HOME/{config,logs,data}
```

**Verify**: `ls -la $GITLAB_HOME` --> should show `config/`, `logs/`, `data/` directories.

### 2. Create the docker-compose.yml

This is the production-ready base configuration for GitLab CE. [src1]

```yaml
# docker-compose.yml -- GitLab CE with Docker Compose
# Requires: Docker Engine 24+, Docker Compose V2
# Host requirements: 4 GB RAM minimum, 8 GB recommended

services:
  gitlab:
    image: gitlab/gitlab-ce:18.7.1-ce.0
    container_name: gitlab
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # External URL -- MUST match your actual domain
        external_url 'https://gitlab.example.com'

        # SSH port (if using non-standard port)
        # gitlab_rails['gitlab_shell_ssh_port'] = 2224

        # Timezone
        gitlab_rails['time_zone'] = 'UTC'

        # Disable services you don't need to save memory
        # prometheus['enable'] = false
        # grafana['enable'] = false
        # alertmanager['enable'] = false
    ports:
      - '80:80'
      - '443:443'
      - '22:22'
    volumes:
      - '${GITLAB_HOME}/config:/etc/gitlab'
      - '${GITLAB_HOME}/logs:/var/log/gitlab'
      - '${GITLAB_HOME}/data:/var/opt/gitlab'
    shm_size: '256m'
    # Resource limits (optional but recommended)
    # deploy:
    #   resources:
    #     limits:
    #       memory: 8G
    #     reservations:
    #       memory: 4G
```

**Verify**: `docker compose config` --> should show the resolved configuration without errors.

### 3. Configure SSL/TLS

Choose one of three approaches depending on your infrastructure. [src2]

**Variant A: Let's Encrypt (automatic)**

```ruby
# Add to GITLAB_OMNIBUS_CONFIG
external_url 'https://gitlab.example.com'
letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['admin@example.com']
letsencrypt['auto_renew'] = true
letsencrypt['auto_renew_hour'] = 2
letsencrypt['auto_renew_day_of_month'] = "*/7"
```

**Variant B: Behind a reverse proxy**

```ruby
# Add to GITLAB_OMNIBUS_CONFIG
external_url 'https://gitlab.example.com'
nginx['listen_port'] = 80
nginx['listen_https'] = false
nginx['proxy_set_headers'] = {
  "X-Forwarded-Proto" => "https",
  "X-Forwarded-Ssl" => "on"
}
```

**Variant C: Own certificates**

```ruby
# Add to GITLAB_OMNIBUS_CONFIG
external_url 'https://gitlab.example.com'
nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.example.com.crt"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.example.com.key"
```

Place certificate files in `$GITLAB_HOME/config/ssl/`.

**Verify**: `curl -I https://gitlab.example.com` --> should return `HTTP/2 200` or `302` with valid TLS.

### 4. Add GitLab Runner for CI/CD

Add the Runner service to your docker-compose.yml and register it. [src4] [src7]

```yaml
# Append to docker-compose.yml services section
  gitlab-runner:
    image: gitlab/gitlab-runner:latest
    container_name: gitlab-runner
    restart: always
    depends_on:
      - gitlab
    volumes:
      - gitlab-runner-config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock

volumes:
  gitlab-runner-config:
```

Register the runner (GitLab 16.0+ uses authentication tokens):

```bash
# 1. In GitLab UI: Admin > CI/CD > Runners > New instance runner
# 2. Copy the runner authentication token (glrt-...)
# 3. Register the runner:
docker exec -it gitlab-runner gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.example.com" \
  --token "glrt-YOUR_TOKEN_HERE" \
  --executor "docker" \
  --docker-image "alpine:latest" \
  --description "docker-runner"
```

**Verify**: `docker exec -it gitlab-runner gitlab-runner list` --> should show the registered runner.

### 5. Configure SMTP for email notifications

GitLab's Docker image does not include an SMTP server. Configure one for notifications, password resets, and CI/CD emails. [src5]

```ruby
# Add to GITLAB_OMNIBUS_CONFIG -- Gmail example
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.gmail.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "your-email@gmail.com"
gitlab_rails['smtp_password'] = "your-app-password"
gitlab_rails['smtp_domain'] = "smtp.gmail.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@example.com'
```

```ruby
# Office 365 / Outlook example
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.office365.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "your-email@company.com"
gitlab_rails['smtp_password'] = "your-password"
gitlab_rails['smtp_domain'] = "company.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['gitlab_email_from'] = 'gitlab@company.com'
```

**Verify**: `docker exec -it gitlab gitlab-rails console -e production` then run `Notify.test_email('you@example.com', 'Test', 'It works!').deliver_now` --> should send a test email.

### 6. Launch and retrieve initial password

Start GitLab and retrieve the auto-generated root password. [src1]

```bash
# Start all services
docker compose up -d

# Wait for GitLab to initialize (takes 3-5 minutes on first start)
docker compose logs -f gitlab | grep -i "ready"

# Retrieve initial root password (auto-deleted after 24 hours)
docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
```

**Verify**: Open `https://gitlab.example.com` in a browser, log in with username `root` and the retrieved password.

### 7. Set up automated backups

Configure a cron job on the host for regular backups. [src3]

```bash
# Create a backup
docker exec -t gitlab gitlab-backup create

# Backup secrets (CRITICAL -- not included in gitlab-backup)
cp $GITLAB_HOME/config/gitlab-secrets.json \
   $GITLAB_HOME/config/gitlab-secrets.json.bak

# Add to crontab for daily backups at 2 AM
# crontab -e
0 2 * * * docker exec -t gitlab gitlab-backup create \
  CRON=1 >> /var/log/gitlab-backup.log 2>&1
0 2 * * * cp /srv/gitlab/config/gitlab-secrets.json \
  /srv/gitlab/config/gitlab-secrets.json.$(date +\%F) 2>&1
```

**Verify**: `ls -la $GITLAB_HOME/data/backups/` --> should show backup tar files named like `*_gitlab_backup.tar`.

## Code Examples

### Complete Production docker-compose.yml

> Full script: [complete-production-docker-compose-yml.yml](scripts/complete-production-docker-compose-yml.yml) (63 lines)

```yaml
# docker-compose.yml -- GitLab CE Production Setup
# Usage: GITLAB_HOME=/srv/gitlab docker compose up -d
# Requirements: Docker Engine 24+, 8 GB RAM, Linux host
services:
  gitlab:
# ... (see full script)
```

### Memory-Constrained Setup (4 GB RAM)

> Full script: [memory-constrained-setup-4-gb-ram.yml](scripts/memory-constrained-setup-4-gb-ram.yml) (37 lines)

```yaml
# docker-compose.yml -- GitLab CE for small/dev servers (4 GB RAM)
# Disables non-essential services to reduce memory footprint
services:
  gitlab:
    image: gitlab/gitlab-ce:18.7.1-ce.0
# ... (see full script)
```

### Restore from Backup

```bash
#!/bin/bash
# restore-gitlab.sh -- Restore GitLab from backup
# Usage: ./restore-gitlab.sh 1708992000_2026_02_27_18.7.1

BACKUP_TIMESTAMP="$1"

if [ -z "$BACKUP_TIMESTAMP" ]; then
  echo "Usage: $0 <backup_timestamp>"
  echo "Example: $0 1708992000_2026_02_27_18.7.1"
  exit 1
fi

# Stop Puma and Sidekiq (keep PostgreSQL running)
docker exec -it gitlab gitlab-ctl stop puma
docker exec -it gitlab gitlab-ctl stop sidekiq

# Verify services are stopped
docker exec -it gitlab gitlab-ctl status

# Restore the backup (must match GitLab version)
docker exec -it gitlab gitlab-backup restore \
  BACKUP="${BACKUP_TIMESTAMP}"

# Restore secrets (CRITICAL)
docker cp gitlab-secrets.json.bak gitlab:/etc/gitlab/gitlab-secrets.json

# Reconfigure and restart
docker exec -it gitlab gitlab-ctl reconfigure
docker exec -it gitlab gitlab-ctl restart

# Verify
docker exec -it gitlab gitlab-rake gitlab:check SANITIZE=true
```

## Anti-Patterns

### Wrong: Using `latest` tag in production

```yaml
# BAD -- latest tag can pull breaking changes on restart
services:
  gitlab:
    image: gitlab/gitlab-ce:latest
```

### Correct: Pin to a specific version

```yaml
# GOOD -- pinned version ensures reproducible deployments
services:
  gitlab:
    image: gitlab/gitlab-ce:18.7.1-ce.0
```

### Wrong: Storing data without volumes

```yaml
# BAD -- data is lost when container is removed
services:
  gitlab:
    image: gitlab/gitlab-ce:18.7.1-ce.0
    # No volumes defined -- all data inside container
```

### Correct: Map all three volumes

```yaml
# GOOD -- persistent data survives container recreation
services:
  gitlab:
    image: gitlab/gitlab-ce:18.7.1-ce.0
    volumes:
      - '${GITLAB_HOME}/config:/etc/gitlab'
      - '${GITLAB_HOME}/logs:/var/log/gitlab'
      - '${GITLAB_HOME}/data:/var/opt/gitlab'
```

### Wrong: Using deprecated registration tokens for Runner

```bash
# BAD -- registration tokens deprecated in GitLab 16.0
docker exec -it gitlab-runner gitlab-runner register \
  --registration-token "DEPRECATED_TOKEN" \
  --url "https://gitlab.example.com"
```

### Correct: Using runner authentication tokens

```bash
# GOOD -- authentication tokens from GitLab 16.0+
# 1. Create runner in UI: Admin > CI/CD > Runners > New instance runner
# 2. Use the generated token (glrt-...)
docker exec -it gitlab-runner gitlab-runner register \
  --token "glrt-YOUR_AUTH_TOKEN" \
  --url "https://gitlab.example.com" \
  --executor "docker" \
  --docker-image "alpine:latest"
```

### Wrong: Skipping shm_size

```yaml
# BAD -- Prometheus may crash with "cannot allocate memory" errors
services:
  gitlab:
    image: gitlab/gitlab-ce:18.7.1-ce.0
    # No shm_size specified
```

### Correct: Setting shm_size explicitly

```yaml
# GOOD -- 256m shared memory prevents Prometheus OOM errors
services:
  gitlab:
    image: gitlab/gitlab-ce:18.7.1-ce.0
    shm_size: '256m'
```

### Wrong: Backing up only with gitlab-backup

```bash
# BAD -- secrets file is NOT included in gitlab-backup
docker exec -t gitlab gitlab-backup create
# Missing: gitlab-secrets.json backup
```

### Correct: Backing up data AND secrets

```bash
# GOOD -- back up both the data and secrets file
docker exec -t gitlab gitlab-backup create
cp $GITLAB_HOME/config/gitlab-secrets.json \
   /backup/gitlab-secrets.json.$(date +%F)
```

## Common Pitfalls

- **Port 22 conflict**: Host SSH daemon already uses port 22, blocking GitLab SSH. Fix: Change host SSH to a different port in `/etc/ssh/sshd_config` or map GitLab SSH to another port: `'2224:22'` and set `gitlab_rails['gitlab_shell_ssh_port'] = 2224`. [src1]
- **Initial password expires**: The auto-generated root password file is deleted after 24 hours on first reconfigure. Fix: Retrieve it immediately with `docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password` and change it in the UI. [src1]
- **Container fails to start with OOM**: GitLab requires significant RAM. Fix: Set `shm_size: '256m'` in compose file, ensure at least 4 GB RAM available, and disable Prometheus/Grafana in memory-constrained environments. [src6]
- **SMTP password with special characters**: Ruby string delimiters in the password (`'`, `"`, `#`, `\`) break the config parser. Fix: Escape special characters or use a password without Ruby string delimiters. [src5]
- **External URL mismatch**: Setting `external_url` to `http://` while GitLab generates `https://` links causes redirect loops. Fix: Ensure `external_url` matches your actual access URL including protocol and port. [src2]
- **Runner can't reach GitLab**: Runner container can't resolve GitLab hostname. Fix: Add the GitLab container's IP to the runner's `/etc/hosts` or use Docker's `extra_hosts` in compose, or ensure both services are on the same Docker network. [src4]
- **Backup restore fails with version mismatch**: GitLab backup can only be restored to the exact same version. Fix: Always note the GitLab version before backup; use `gitlab/gitlab-ce:VERSION-ce.0` matching the backup's version. [src3]
- **SELinux blocking volume mounts**: On RHEL/Fedora, SELinux prevents container access to host volumes. Fix: Append `:Z` to volume mounts: `'${GITLAB_HOME}/config:/etc/gitlab:Z'`. [src1]

## Diagnostic Commands

> Full script: [diagnostic-commands.sh](scripts/diagnostic-commands.sh) (26 lines)

```bash
# Check GitLab container status and health
docker compose ps
docker inspect gitlab --format='{{.State.Health.Status}}'
# View GitLab logs (all components)
docker compose logs -f gitlab
# ... (see full script)
```

## Version History & Compatibility

| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| 18.x (current) | Active | New UI, Duo Analytics | Direct upgrade from 17.x supported |
| 17.x | Maintenance | Requires PostgreSQL 14+ | Upgrade PostgreSQL before upgrading from 16.x |
| 16.x | EOL | Runner registration tokens deprecated | Switch to runner authentication tokens |
| 15.x | EOL | Praefect/Gitaly changes | Must stop at 15.11.x before jumping to 16.x |
| 14.x | EOL | Background migrations required | Must complete all migrations before upgrading |

**Upgrade path**: GitLab requires sequential major version upgrades. You cannot skip major versions. Follow the official upgrade path: 14.x --> 15.x --> 16.x --> 17.x --> 18.x.

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Small to mid-size team (< 500 users) self-hosting | Kubernetes infrastructure available | GitLab Helm Chart or GitLab Operator |
| Single-server deployment | High availability required (multiple nodes) | GitLab HA reference architecture |
| Quick local development/testing setup | Enterprise features needed (SAML SSO, Compliance) | GitLab EE Docker image |
| Air-gapped / on-premise requirement | SaaS is acceptable for your org | gitlab.com (hosted) |
| Full DevOps platform (SCM + CI/CD + Registry) | Only need Git hosting (lightweight) | Gitea or Gogs Docker |

## Important Caveats

- GitLab CE omnibus container runs PostgreSQL, Redis, Nginx, Puma, Sidekiq, Gitaly, and Prometheus all in one container -- this is convenient but not a microservices architecture. For large deployments, use separate services.
- Upgrading GitLab Docker requires pulling the new image and recreating the container (`docker compose pull && docker compose up -d`). Always back up before upgrading.
- The `GITLAB_OMNIBUS_CONFIG` environment variable is evaluated on every container start but is NOT persisted to `gitlab.rb`. If you edit `gitlab.rb` directly inside the container, those changes persist across restarts but may conflict with `GITLAB_OMNIBUS_CONFIG`.
- Docker Compose V1 (`docker-compose`) is deprecated. Use Docker Compose V2 (`docker compose` without hyphen) instead.
- GitLab Pages and Mattermost are bundled but disabled by default. Enable them via `gitlab.rb` settings if needed.
- Container Registry requires a separate domain or subdomain (e.g., `registry.example.com`) with its own SSL certificate.

## Related Units
<!-- Generated from related_kos frontmatter -->

- [Docker Compose Fundamentals](/software/devops/docker-compose-basics/2026)
- [Gitea Docker Compose Setup](/software/devops/gitea-docker/2026)
- [GitLab CI/CD Pipeline Configuration](/software/devops/gitlab-ci-pipelines/2026)
