GitLab Self-hosting Guide on DigitalOcean Droplet 2026
ระบบเก็บซอร์สโค้ดแบบ self-host บน Droplet จากเลือกสเปก ติดตั้ง ตั้งค่า SSL ไปจนถึง backup และข้อควรระวังเรื่องทรัพยากร
GitLab Community Edition is an alternative for teams wanting to keep source code and CI/CD pipelines on their own infrastructure instead of relying on GitLab.com or GitHub. Running GitLab on a DigitalOcean Droplet can be done via either the Marketplace 1-Click App or Docker, but you must first understand GitLab's resource constraints before choosing a spec, since it combines Postgres, Redis, Sidekiq, and Gitaly all on a single machine.
Contents
Minimum Droplet Specs for GitLab CE
GitLab's official documentation specifies a minimum of 4 GB RAM and 2 CPU cores for an Omnibus installation, suited for small teams with up to roughly 20 users. However, GitLab itself is clear that this number represents "the bare minimum to run"—not a recommended spec for actual production use. The gitlab-ctl reconfigure process and running Puma, Sidekiq, PostgreSQL, Redis, and Gitaly concurrently on a single machine consume far more memory than expected. Especially during initial reconfiguration or when running heavy CI jobs, GitLab recommends at least 8 GB RAM as the safer standard for a single-node instance meant for real-world use, not just testing. Comparing this to DigitalOcean's Droplet tiers, the 4 GiB RAM / 2 vCPU / 80 GB SSD / 4,000 GiB transfer plan at $24/month exactly matches GitLab's stated minimum, but feels constrained with concurrent users or CI runners. The 8 GiB RAM / 4 vCPU / 160 GB SSD / 5,000 GiB transfer plan at $48/month strikes a better balance for teams of 10–20 actively using it daily, giving Sidekiq and PostgreSQL enough headroom to avoid constant swapping. For storage, beyond the SSD included with the Droplet, repositories, CI artifacts, and Container Registry images grow faster than anticipated. Provisioning DigitalOcean Volumes at $0.10 per GiB per month and moving /var/opt/gitlab/git-data there lets you resize independently without migrating the entire Droplet.
- GitLab specifies a minimum of 4 GB RAM + 2 vCPU for ~20 users, but recommends 8 GB+ for production use.
- 4 GiB/2 vCPU/80 GB SSD Droplet at $24/month meets minimum specs; suitable for testing or very small teams.
- 8 GiB/4 vCPU/160 GB SSD Droplet at $48/month is the sweet spot for teams using it actively every day.
- Separate repository/artifact storage to DigitalOcean Volumes ($0.10/GiB/month) for flexible resizing independent of the Droplet.
Installation via Marketplace 1-Click App or Docker
One thing that surprised us: digitalOcean includes GitLab CE in its Marketplace, ready to select when creating a Droplet. The system automatically installs the Omnibus package on Ubuntu. The advantage: no manual installation commands—power on and GitLab is ready immediately after cloud-init finishes, typically 5–10 minutes. On first SSH, you'll find instructions to set your external URL via /etc/gitlab/gitlab.rb, then run gitlab-ctl reconfigure to apply changes. The alternative is Docker, better for those wanting full control over volumes, networking, and resource limits. A docker-compose.yml service named gitlab uses the official gitlab/gitlab-ce:latest image, with hostname set to your real domain upfront. Map ports '80:80', '443:443', and '22:22' (or change the host SSH port if port 22 is already in use), then mount three volumes: /etc/gitlab, /var/log/gitlab, and /var/opt/gitlab to host paths so data persists when the container is recreated. Both methods ultimately run the same Omnibus GitLab software; Marketplace installs on bare OS while Docker runs in a container, making the latter easier to move or backup via volume copies. Once the container is ready, set the root password via the web UI or check the temporary password GitLab generates in /etc/gitlab/initial_root_password, which is auto-deleted within 24 hours.
- Marketplace 1-Click App deploys Omnibus GitLab on Ubuntu automatically; ready in 5–10 minutes.
- Docker setup uses
gitlab/gitlab-ce:latestimage, mapping ports 80/443/22 and mounting /etc/gitlab, /var/log/gitlab, /var/opt/gitlab volumes. - Both methods run the same Omnibus GitLab; difference is bare OS install vs. containerized.
- Initial root password is in
/etc/gitlab/initial_root_password, auto-deleted after 24 hours.
Domain and SSL Configuration
Before setting up SSL, point your domain or subdomain's A record (e.g., git.example.com) to the Droplet IP and wait for propagation, since the Let's Encrypt certificate request relies on HTTP challenge validation from that IP. For Omnibus installations (Marketplace or Docker), edit /etc/gitlab/gitlab.rb to set external_url 'https://git.example.com' and enable built-in Let's Encrypt with letsencrypt['enable'] = true. Then run gitlab-ctl reconfigure again. The system automatically requests and installs the certificate, plus sets up a cron job to renew it. You won't need a separate Certbot installation like regular websites. One caveat: if your Droplet sits behind Cloudflare with proxy enabled (orange cloud), the HTTP challenge may fail because Let's Encrypt sees Cloudflare's IP, not your Droplet's real one. Either temporarily disable the proxy during the first certificate request, or use DNS challenge instead. For Docker deployments with Nginx or another reverse proxy in front already, disable SSL management inside GitLab and let the proxy terminate SSL instead to avoid conflicting dual-layer configs. After setup, test pushing and pulling over HTTPS and SSH to verify port 22 (or whatever you assigned for git over SSH) still works, since changing external_url and SSH ports simultaneously sometimes breaks existing client connections.
- Point the domain A record to the Droplet IP and wait for propagation before requesting certificates.
- Set
external_urlandletsencrypt['enable'] = trueingitlab.rb, then rungitlab-ctl reconfigure. - If using Cloudflare proxy (orange cloud), temporarily disable it or use DNS challenge when requesting the certificate.
- If a reverse proxy is already in front of the Docker container, disable SSL inside GitLab to avoid conflicting configurations.
Backup and Restore GitLab
GitLab includes a built-in backup tool via Rake tasks. The main command, gitlab-rake gitlab:backup:create, bundles repositories, databases, uploads, CI artifacts, and registry images (if configured) into a single tar file stored at /var/opt/gitlab/backups by default. Important: this backup does not include configuration files or secret keys. You must separately back up /etc/gitlab/gitlab.rb and /etc/gitlab/gitlab-secrets.json because without the secrets file, you cannot decrypt credentials stored in the original database even if the restore succeeds. Restore via gitlab-rake gitlab:backup:restore BACKUP=timestamp, specifying the timestamp from the backup filename. Critical: the GitLab version must match exactly between backup creation and restore; schema changes vary per version, and restoring across versions often fails. For production, set up a cron job to run backups daily and immediately copy the backup files and secrets outside the Droplet—never keep them on the same disk as production. Use DigitalOcean Spaces with s3cmd or rclone to sync backups, or take Droplet Snapshots at $0.06 per GiB per month, especially before major upgrades, since snapshots let you roll back the entire system faster than piecing it back together with Rake restore.
- Create backups with
gitlab-rake gitlab:backup:create; files go to/var/opt/gitlab/backups. - Backups exclude config files; always keep
gitlab.rbandgitlab-secrets.jsonseparate. - Restore with
gitlab-rake gitlab:backup:restore BACKUP=timestampusing the same GitLab version as the backup.
Resource Constraints to Watch For
Most people underestimate that Omnibus bundles multiple services on one machine: PostgreSQL, Redis, Sidekiq for background jobs, Gitaly for repository management, Puma for web serving, and Container Registry if enabled. They all compete for memory and CPU on a single Droplet. On a 4 GB Droplet, running heavy CI/CD jobs alongside active users often leaves Sidekiq queues backed up or the web interface sluggish. The most common symptom is the system swapping hard, turning page loads from milliseconds into seconds, or gitlab-ctl reconfigure hanging mid-step. If using a GitLab Runner for CI/CD, run it on a separate Droplet, never on the main server, because heavy CI jobs can saturate CPU and disrupt concurrent users pushing or pulling code. Disk space is another trap—not just repositories but CI artifacts, logs, and Container Registry images accumulate far faster than expected. Without a retention policy to clean old artifacts automatically, the disk fills silently and GitLab halts without warning. Finally, upgrading versions comes with constraints: GitLab enforces a specific upgrade path you must follow sequentially; skipping multiple versions in one jump risks database migration failure. Always read release notes and plan incremental upgrades rather than leaving it months and then jumping all at once.
- PostgreSQL, Redis, Sidekiq, Gitaly, and Puma all run together, competing for resources when load spikes.
- Keep GitLab Runner on a separate Droplet, never on the main server, to avoid CI jobs starving concurrent users.
- Set retention policies to auto-delete old CI artifacts and registry images, preventing unexpected disk fills.
When to Use Self-Hosted GitLab (Real Use Cases)
Self-hosting GitLab on a Droplet makes sense for teams with clear reasons to keep code and data on their own infrastructure. Examples: data residency regulations requiring source code to stay in a specific geographic region, or corporate security policies forbidding code storage on third-party SaaS. Another common case is CI/CD runners needing hardware or network access only available internally—easier to manage when GitLab runs on the same private network. Small-to-mid teams seeking premium features like approval rules or advanced CI/CD, without paying per-seat SaaS licensing, can use CE self-hosted and skip the monthly subscription, trading operational overhead for feature access. Conversely, teams with 20–30+ active users and true high-availability needs outgrow a single Droplet. They need the reference architecture spreading Gitaly, PostgreSQL, and Redis across separate machines, or they should consider GitLab.com SaaS if no compliance requirement forces self-hosting. For startups or small dev teams with one or two ops people, starting with a single Droplet per this guide, then scaling incrementally as needed, beats over-engineering complex infrastructure from day one.
- Ideal for teams with data residency or compliance rules forbidding third-party SaaS for source code.
- Suits CI/CD requiring direct access to internal hardware or networks.
- Cost-effective for small teams wanting premium features without per-seat SaaS fees, provided they're willing to manage operations.
- Teams exceeding 20–30 users or needing high availability should graduate to multi-machine reference architecture or SaaS.
Common Mistakes and How to Fix Them
The most frequent issue is gitlab-ctl reconfigure hanging or failing on Droplets with less than 4 GB RAM, usually because PostgreSQL migration runs out of memory. A quick fix: add temporary swap space, then retry reconfigure. Long-term: upgrade the Droplet to 4–8 GB RAM. Another common trap is forgetting to open required ports in DigitalOcean's Cloud Firewall; default rules often allow only SSH, blocking HTTP/HTTPS access. Add rules for ports 80 and 443 (and 5050 for Container Registry if enabled). SSL certificate requests frequently fail when DNS hasn't fully propagated or Cloudflare proxy masks the real IP. Verify with dig or nslookup that your domain resolves to the Droplet's IP before retrying reconfigure. Backup restores fail when the installed GitLab version doesn't match the backup's creation version; always install the original version first, restore, then upgrade afterward—never restore across versions. Finally, disk fills silently from CI artifact accumulation. Fix by setting artifact expiration policies in each project's CI/CD settings or periodically running Rake tasks to clean expired artifacts.
- reconfigure hangs/fails on low RAM → add temporary swap, then upgrade Droplet to 4–8 GB long-term.
- Can't reach the web interface even though Droplet is running → check Cloud Firewall has ports 80/443 open (and 5050 for Registry).
- SSL certificate request fails → verify DNS propagation with dig/nslookup and temporarily disable Cloudflare proxy if needed.
Best Practices
Start with a Droplet spec that leaves headroom for future growth rather than the bare minimum, since resizing requires a reboot that impacts all active users. Set up daily automated backups via gitlab-rake gitlab:backup:create in cron and immediately sync the backup files along with gitlab-secrets.json and gitlab.rb to an external location like DigitalOcean Spaces using s3cmd or rclone. Never leave backups on the same disk as production. Take a Droplet Snapshot before every major version upgrade for fast rollback if something breaks—much quicker than Rake restore, which takes longer to reassemble. Enable DigitalOcean's Cloud Firewall and restrict SSH access to known IPs only, not the entire internet. Turn on 2FA for GitLab's root and admin accounts. Use DigitalOcean Monitoring to set alerts that notify you when RAM or disk usage exceeds thresholds, so you know before the system crashes. As the team grows and CI/CD jobs intensify, spin up a separate GitLab Runner Droplet early, before the main server's CPU becomes the bottleneck. Finally, monitor GitLab's release notes regularly and plan incremental upgrades on a consistent schedule rather than skipping versions for months then attempting a massive jump—the latter is far riskier.
- Choose a Droplet with headroom for growth; resizing requires a reboot affecting all users.
- Automate daily backups via cron, immediately sync to external storage (Spaces, Rclone), include gitlab-secrets.json always.
- Take Droplet Snapshots before major upgrades for faster rollback than Rake restore.
- Restrict SSH via Cloud Firewall, enable 2FA for admin accounts of GitLab.
- Set up DigitalOcean Monitoring alerts for RAM/disk before the system runs out.
Frequently Asked Questions
gitlab-rake gitlab:backup:create at your preferred time (e.g., nightly), then add a step in the same script to copy the backup and gitlab-secrets.json to external storage like DigitalOcean Spaces via s3cmd or rclone so backups don't sit only on the production disk.