How to Self-Host GitLab CE with Docker Compose
GitLab CE is the heaviest self-hosted Git platform you can run — and the most complete. If you need CI/CD, a container registry, issue boards, and wikis in a single deployment, nothing else comes close. But it demands 4 GB of RAM minimum and takes several minutes to start. Know what you’re getting into before deploying.
What Is GitLab CE?
GitLab Community Edition is an open-source DevOps platform that provides Git repository hosting, continuous integration/delivery (CI/CD), issue tracking, a container registry, wikis, and package management. It’s the self-hosted version of gitlab.com, minus the premium features (which require GitLab EE). GitLab CE replaces GitHub, Bitbucket, and separate CI/CD tools like Jenkins or GitHub Actions with a single application.
Updated March 2026: Verified with latest Docker images and configurations.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 4 GB of RAM minimum (8 GB recommended — GitLab is resource-intensive)
- 2+ CPU cores
- 30 GB of free disk space (excluding repository data)
- A domain name (strongly recommended)
Docker Compose Configuration
Create a docker-compose.yml file:
services:
gitlab:
image: gitlab/gitlab-ce:18.9.2-ce.0
container_name: gitlab
restart: unless-stopped
hostname: gitlab.yourdomain.com
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.yourdomain.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2424
gitlab_rails['time_zone'] = 'UTC'
gitlab_rails['lfs_enabled'] = true
gitlab_rails['backup_keep_time'] = 604800
# Disable monitoring to save ~300 MB RAM
prometheus_monitoring['enable'] = false
alertmanager['enable'] = false
gitlab_exporter['enable'] = false
node_exporter['enable'] = false
postgres_exporter['enable'] = false
prometheus['enable'] = false
puma['exporter_enabled'] = false
redis_exporter['enable'] = false
sidekiq['metrics_enabled'] = false
# Reduce memory usage for small teams
puma['worker_processes'] = 0
sidekiq['concurrency'] = 10
gitlab_kas['enable'] = false
# Aggressive memory reclamation
gitlab_rails['env'] = {
'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000'
}
ports:
- "80:80"
- "443:443"
- "2424:22"
volumes:
- gitlab_config:/etc/gitlab
- gitlab_logs:/var/log/gitlab
- gitlab_data:/var/opt/gitlab
shm_size: '256m'
volumes:
gitlab_config:
gitlab_logs:
gitlab_data:
The GITLAB_OMNIBUS_CONFIG block accepts any setting from GitLab’s gitlab.rb configuration file. Settings passed here are evaluated at container startup and override the persistent configuration.
Start the stack:
docker compose up -d
GitLab takes 3-5 minutes to initialize on first startup. Monitor progress with:
docker logs -f gitlab
Initial Setup
- Retrieve the auto-generated root password:
docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
This file is deleted automatically after 24 hours — copy the password now.
- Open
https://gitlab.yourdomain.comin your browser - Log in with username
rootand the password from step 1 - Change the root password immediately: User Settings → Password
- Disable open sign-up: Admin → Settings → General → Sign-up restrictions → uncheck “Sign-up enabled”
Configuration
Volume Mounts
| Volume | Container Path | Contents |
|---|---|---|
gitlab_config | /etc/gitlab | Configuration files (gitlab.rb, secrets) |
gitlab_logs | /var/log/gitlab | Application and service logs |
gitlab_data | /var/opt/gitlab | Repositories, uploads, database, CI artifacts |
Key GITLAB_OMNIBUS_CONFIG Settings
| Setting | Purpose |
|---|---|
external_url | Your GitLab URL (determines HTTP vs HTTPS) |
gitlab_rails['gitlab_shell_ssh_port'] | SSH port (set to match your host mapping) |
gitlab_rails['lfs_enabled'] | Enable Git Large File Storage |
gitlab_rails['backup_keep_time'] | How long to keep backups (seconds) |
puma['worker_processes'] | Web server workers (0 = single-process mode) |
sidekiq['concurrency'] | Background job concurrency (default: 20) |
prometheus_monitoring['enable'] | Toggle built-in monitoring stack |
gitlab_kas['enable'] | Kubernetes Agent Server (disable if unused) |
SMTP Configuration
GitLab does not include an SMTP server. Add email configuration to GITLAB_OMNIBUS_CONFIG:
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.yourdomain.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "[email protected]"
gitlab_rails['smtp_password'] = "your-smtp-password"
gitlab_rails['smtp_domain'] = "yourdomain.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['gitlab_email_from'] = '[email protected]'
Container Registry
GitLab includes a Docker container registry. Enable it in GITLAB_OMNIBUS_CONFIG:
registry_external_url 'https://registry.yourdomain.com'
This requires a separate domain or subdomain with its own SSL certificate.
Reverse Proxy
If running GitLab behind a reverse proxy instead of using its built-in nginx, add to GITLAB_OMNIBUS_CONFIG:
nginx['listen_port'] = 80
nginx['listen_https'] = false
nginx['proxy_set_headers'] = {
"X-Forwarded-Proto" => "https",
"X-Forwarded-Ssl" => "on"
}
Map port 80:80 only and let your reverse proxy handle SSL.
See Reverse Proxy Setup for full configuration.
Backup
GitLab has a built-in backup command that creates a tar archive of all data:
docker exec -t gitlab gitlab-backup create
Backups are stored in /var/opt/gitlab/backups/ (inside the gitlab_data volume). Also back up the config volume separately — it contains secrets needed for restore:
docker exec -t gitlab tar cf - /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab.rb > gitlab_config_backup.tar
See Backup Strategy for automated backup workflows.
Troubleshooting
GitLab takes forever to start
Symptom: Container stays unhealthy for 5+ minutes, web UI inaccessible. Fix: GitLab legitimately takes 3-5 minutes to start. If it exceeds 10 minutes, check RAM — GitLab needs at least 4 GB. Monitor with:
docker exec -it gitlab gitlab-ctl status
If services keep restarting, add swap space or increase RAM.
502 Bad Gateway after startup
Symptom: Web UI shows 502 errors intermittently after container starts.
Fix: Puma (the web server) may still be initializing. Wait 2-3 more minutes. If persistent, check if shm_size is set to at least 256m in your Compose file — Prometheus metrics require this shared memory allocation.
SSH connections refused on port 22
Symptom: git clone via SSH fails with “connection refused.”
Fix: Port 22 on the host is likely used by the host’s own SSH daemon. Map GitLab’s SSH to a different host port (e.g., 2424:22) and set gitlab_rails['gitlab_shell_ssh_port'] = 2424 in GITLAB_OMNIBUS_CONFIG. Users clone with git clone ssh://[email protected]:2424/user/repo.git.
Out of memory (OOM) kills
Symptom: Container crashes or services restart randomly. dmesg shows OOM killer messages.
Fix: Apply the memory optimizations in the Docker Compose example above: disable Prometheus monitoring, set puma['worker_processes'] = 0, reduce sidekiq['concurrency'], and disable gitlab_kas. These save 400-700 MB of RAM. If still insufficient, add 2 GB of swap.
Resource Requirements
- RAM: 2 GB absolute minimum (with all optimizations), 4 GB recommended for small teams, 8+ GB for 50+ users
- CPU: 2 cores minimum, 4+ recommended
- Disk: 10 GB for the application, 20+ GB for PostgreSQL, plus repository storage
Verdict
GitLab CE is the most feature-complete self-hosted DevOps platform — nothing else gives you Git hosting, CI/CD, container registry, issue tracking, wikis, and package management in a single container. The trade-off is resource consumption: 4 GB of RAM for a small team is steep compared to Gitea at 200 MB or Forgejo at similar levels. For teams that need integrated CI/CD and are willing to pay the resource cost, GitLab CE is the right choice. For teams that just need Git hosting with pull requests, Gitea or Forgejo are 20x lighter and perfectly capable.
Frequently Asked Questions
How much RAM does GitLab CE actually need?
With the memory optimizations in the Docker Compose above (disabled Prometheus, single Puma process, reduced Sidekiq concurrency), GitLab runs in 2-3 GB for a small team (1-5 users). Without optimizations, expect 4-6 GB. For 50+ users, plan for 8+ GB. GitLab is the heaviest self-hosted Git server by a wide margin.
Can I use GitLab CI/CD without GitLab Runners?
No. GitLab CI/CD requires at least one GitLab Runner to execute pipeline jobs. The runner is a separate process — install it on the same machine or a different one. The simplest option is the Docker executor, which runs each job in an isolated container.
What’s the difference between GitLab CE and GitLab EE?
GitLab EE (Enterprise Edition) includes premium features like advanced security scanning (SAST/DAST), approval rules, burndown charts, and portfolio management. GitLab CE covers repositories, CI/CD, issue tracking, container registry, and wikis. For most small to medium teams, CE has everything you need.
Can I migrate from GitHub to GitLab CE?
Yes. GitLab has a built-in GitHub import tool under New Project → Import project → GitHub. It migrates repositories, issues, pull requests, and wikis. For large organizations, use the GitLab API for bulk migration.
Is GitLab CE suitable for a single developer?
It works but is overkill. GitLab CE uses 2-3 GB of RAM even with optimizations — that’s a lot for one person’s Git hosting. Gitea or Forgejo provide Git hosting, issues, and pull requests at ~100 MB RAM. Use GitLab only if you specifically need its CI/CD or container registry.
Can I run GitLab CE on a Raspberry Pi?
Not practically. GitLab needs a minimum of 2 GB RAM (with aggressive optimizations) and performs poorly on ARM processors. Even a Pi 5 with 8 GB RAM would struggle. Use Gitea or Forgejo instead — they run well on a Raspberry Pi.
Related
- Forgejo vs GitLab CE: Which Git Server to Self-Host?
- GitLab CE vs Gitea: Which Should You Self-Host?
- OneDev vs GitLab CE: Which Should You Self-Host?
- How to Self-Host Gitea — lightweight Git hosting
- How to Self-Host Forgejo — community fork of Gitea
- Gitea vs Forgejo — choosing between the lightweight options
- Best Self-Hosted Git Hosting — full category roundup
- Docker Compose Basics — prerequisite guide
- Reverse Proxy Setup — remote access configuration
- Backup Strategy — protect your data
- Replace GitHub — migration guide
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments