How to Install GitLab on a VPS

Running your own Git server sounds like overkill — until you’ve had a private project show up in a data breach, hit a platform’s storage limit at the worst possible time, or lost access to a repo because a third-party service went dark. GitLab CE gives you a full-featured DevOps platform: repositories, issue tracking, CI/CD pipelines, a container registry, and more. And when you self-host it on a VPS with Docker, you own every bit of it.

This guide walks you through deploying GitLab Community Edition on a VPS using Docker and Docker Compose. You’ll end up with a production-ready instance sitting behind Nginx, protected by a Let’s Encrypt SSL certificate, accessible over both HTTPS and SSH.

What Is GitLab CE?

GitLab CE (Community Edition) is the open-source version of GitLab — a self-hosted alternative to platforms like GitHub and Bitbucket. Think of it as a private GitHub you control entirely. It ships with Git repository hosting, merge requests, CI/CD via GitLab Runners, a built-in container registry, wikis, issue boards, and a web IDE. All free. All on your hardware.

The Docker image bundles GitLab’s entire stack — Puma (the web server), Sidekiq (background jobs), PostgreSQL, Redis, and Nginx — into a single container. That makes deployment straightforward but also means the resource requirements are real. GitLab is not a lightweight app. Budget your VPS accordingly.

Prerequisites

Before you start, make sure your server meets these requirements. GitLab CE is one of the heavier self-hosted apps out there — the official docs recommend at least 4 GB of RAM for a single-user or small-team instance.

Requirement Minimum Recommended
CPU 2 vCPUs 4 vCPUs
RAM 4 GB 8 GB
Disk 20 GB SSD 50+ GB SSD
OS Ubuntu 22.04 LTS Ubuntu 22.04 LTS
Docker 24.x Latest stable
Docker Compose v2.x Latest stable
Domain name Required Required
Open ports 80, 443, 22 80, 443, 22

You’ll also need a domain name pointed at your VPS’s public IP address before running Certbot. DNS changes can take a few minutes to propagate, so set that up first.

How to Install GitLab CE on a VPS with Docker

Step 1: Update Your Server and Install Dependencies

Log into your VPS as a non-root sudo user and bring the system up to date. Fresh installs often ship with outdated packages and skipping this step invites subtle compatibility issues down the road.

sudo apt update && sudo apt upgrade -y

Install a handful of utilities you’ll need throughout this guide:

sudo apt install -y curl gnupg2 ca-certificates lsb-release ufw

Step 2: Install Docker and Docker Compose

Skip the version in Ubuntu’s default repos — it’s almost always outdated. Pull Docker straight from the official Docker repository instead.

Add Docker’s GPG key and repository:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] /
  https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | /
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Now install Docker Engine and the Compose plugin:

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Add your user to the docker group so you don’t need to prefix every command with sudo:

sudo usermod -aG docker $USER
newgrp docker

Confirm both tools are working:

docker --version
docker compose version

Step 3: Configure the Firewall

GitLab needs three ports open: 80 (HTTP, used for the Let’s Encrypt challenge), 443 (HTTPS web traffic), and 22 (SSH for Git operations). Lock everything else down.

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp
sudo ufw enable

Check the status to confirm the rules applied:

sudo ufw status

One important note: port 22 is being shared between your server’s own SSH daemon and GitLab’s SSH listener. You’ll remap one of them in the Compose file later so they don’t conflict.

Step 4: Create the Project Directory and Compose File

Keep things tidy by giving GitLab its own directory:

mkdir -p ~/gitlab && cd ~/gitlab

Create the Docker Compose file:

nano docker-compose.yml

Paste in the following configuration. Replace gitlab.yourdomain.com with your actual domain everywhere it appears:

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: always
    hostname: gitlab.yourdomain.com
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://gitlab.yourdomain.com'
        gitlab_rails['gitlab_shell_ssh_port'] = 2222
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        nginx['proxy_set_headers'] = {
          "X-Forwarded-Proto" => "https",
          "X-Forwarded-Ssl" => "on"
        }
        gitlab_rails['time_zone'] = 'UTC'
        gitlab_rails['backup_keep_time'] = 604800
    ports:
      - '127.0.0.1:8080:80'
      - '2222:22'
    volumes:
      - ./config:/etc/gitlab
      - ./logs:/var/log/gitlab
      - ./data:/var/opt/gitlab
    shm_size: '256m'

A few things worth explaining here. The external_url tells GitLab what URL it lives at — this affects every link it generates, from clone URLs to email notifications. Setting nginx['listen_https'] = false and nginx['listen_port'] = 80 is deliberate: GitLab’s internal Nginx handles plain HTTP while the external Nginx you’ll configure next handles SSL termination. Binding the web port to 127.0.0.1:8080 keeps it off the public internet entirely. GitLab’s SSH listener remaps to port 2222 on the host so it doesn’t collide with your server’s own SSH service on port 22.

Step 5: Install Nginx and Certbot

Nginx acts as the public-facing reverse proxy. It receives HTTPS traffic, terminates SSL, and forwards clean HTTP requests to the GitLab container.

sudo apt install -y nginx certbot python3-certbot-nginx

Create an Nginx server block for GitLab. This initial configuration handles the Let’s Encrypt HTTP challenge — Certbot will add SSL directives automatically once it issues a certificate.

sudo nano /etc/nginx/sites-available/gitlab

Add this configuration, again substituting your real domain:

server {
    listen 80;
    server_name gitlab.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # GitLab needs larger upload sizes for repositories
        client_max_body_size 512m;
        proxy_read_timeout 3600s;
        proxy_connect_timeout 300s;
    }
}

Enable the site and test the configuration:

sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 6: Obtain an SSL Certificate with Certbot

With Nginx running and DNS resolving to your server, Certbot can now issue a certificate and automatically update your Nginx config to use it:

sudo certbot --nginx -d gitlab.yourdomain.com

Follow the prompts. When asked whether to redirect HTTP to HTTPS, choose option 2 (Redirect). Certbot will rewrite your Nginx config to add the SSL block and set up automatic renewal via a systemd timer.

Verify the renewal timer is active:

sudo systemctl status certbot.timer

Step 7: Start GitLab

Head back to your GitLab project directory and bring the container up:

cd ~/gitlab
docker compose up -d

GitLab takes a while to initialize — typically 3 to 5 minutes on first boot. It’s running database migrations, generating secrets, and configuring its internal services. Grab a coffee. Watch the logs to see what it’s doing:

docker compose logs -f gitlab

You’re looking for a line like gitlab Reconfigured! followed by the Puma and Sidekiq services starting up cleanly. Once that appears, GitLab is ready.

Step 8: Retrieve the Initial Root Password

GitLab generates a random password for the root admin account on first startup. Retrieve it before it expires (it’s only valid for 24 hours):

docker exec gitlab grep 'Password:' /etc/gitlab/initial_root_password

Copy that password. Now navigate to https://gitlab.yourdomain.com in your browser and sign in with username root and the password you just retrieved.

The first thing you should do after logging in is change the root password. Go to User Settings → Password and set something strong. Also consider disabling public sign-ups under Admin → Settings → General → Sign-up restrictions unless you’re running a public instance.

Step 9: Configure SSH Access for Git Operations

Remember that GitLab’s SSH listener is on port 2222 on your host. Your server’s own SSH daemon still holds port 22. To use SSH-based Git cloning, users need to tell their SSH client which port to use.

On the client machine (not the server), add this to ~/.ssh/config:

Host gitlab.yourdomain.com
    Hostname gitlab.yourdomain.com
    User git
    Port 2222

Add your SSH public key to GitLab by going to User Settings → SSH Keys and pasting in the contents of your ~/.ssh/id_ed25519.pub (or id_rsa.pub). Once that’s saved, test the connection:

ssh -T [email protected]

A successful response looks like: Welcome to GitLab, @yourusername!

How to Update GitLab CE

GitLab releases updates frequently and staying current matters — both for security patches and new features. The update process with Docker is straightforward but GitLab enforces a strict upgrade path. You can’t skip major versions. Always check the official upgrade path docs before jumping between major releases.

That said, for minor and patch updates within the same major version, here’s the process:

Pull the latest image:

cd ~/gitlab
docker compose pull

Take a backup first (you’ll thank yourself later if something goes sideways):

docker exec -t gitlab gitlab-backup create

Stop the running container and bring it back up with the new image:

docker compose down
docker compose up -d

Watch the logs during startup to confirm the update applied cleanly:

docker compose logs -f gitlab

For major version upgrades (say, moving from 16.x to 17.x), consult the upgrade path documentation and use a pinned image tag rather than latest. Pin specific versions in your docker-compose.yml like gitlab/gitlab-ce:17.0.0-ce.0 and step through each required intermediate version one at a time.

Troubleshooting

GitLab Takes Too Long to Start or Stays in a Restart Loop

This almost always comes down to insufficient RAM. GitLab needs at least 4 GB. Check your container’s resource usage:

docker stats gitlab

If memory is maxed out, check whether your VPS has swap configured. Adding 4 GB of swap gives the system breathing room during peak load:

sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Also check the logs for specific error messages before assuming it’s a resource issue:

docker compose logs gitlab 2>&1 | tail -100

502 Bad Gateway After Navigating to Your Domain

A 502 from Nginx means it can reach your server but can’t connect to the upstream GitLab container. Two likely culprits: GitLab is still starting up (it takes several minutes), or the port mapping is wrong.

Verify the container is actually listening on port 8080 locally:

curl -I http://127.0.0.1:8080

If that times out, GitLab isn’t up yet. If it returns a response but Nginx still throws a 502, double-check your proxy_pass directive in the Nginx config and make sure you reloaded Nginx after any changes:

sudo nginx -t && sudo systemctl reload nginx

SSH Git Operations Fail or Time Out

If git clone or git push over SSH hangs or gives a “connection refused” error, the SSH config on your client machine is probably missing or using the wrong port. Confirm port 2222 is open on the firewall:

sudo ufw status | grep 2222

Test the SSH connection directly with verbose output to see exactly where it fails:

ssh -vT -p 2222 [email protected]

If you see a “Host key verification failed” error after a GitLab update, the container regenerated its host keys. Remove the old known_hosts entry on your client and reconnect:

ssh-keygen -R "[gitlab.yourdomain.com]:2222"

Clone URLs Show the Wrong Port or Protocol

GitLab’s web UI generates clone URLs based on external_url and gitlab_shell_ssh_port in your Omnibus config. If clone URLs look wrong — say, showing HTTP instead of HTTPS, or the wrong SSH port — the fix lives in your docker-compose.yml under GITLAB_OMNIBUS_CONFIG.

Make sure these two lines are present and correct:

external_url 'https://gitlab.yourdomain.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2222

After editing the Compose file, restart the container to apply the new config:

docker compose down && docker compose up -d

Email Notifications Aren’t Sending

GitLab’s internal Sendmail setup rarely works out of the box on VPS environments — most cloud providers block outbound port 25. The reliable approach is to configure an external SMTP provider (Gmail, SendGrid, Mailgun, etc.) via GITLAB_OMNIBUS_CONFIG.

Add these lines to your Omnibus config block in docker-compose.yml, substituting your SMTP provider’s details:

gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.sendgrid.net"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "apikey"
gitlab_rails['smtp_password'] = "YOUR_API_KEY"
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]"

Restart the container and test delivery from inside the container:

docker exec -it gitlab gitlab-rails runner "Notify.test_email('[email protected]', 'Test', 'Hello').deliver_now"

What to Do Next

Your GitLab instance is up and running. Here’s what’s worth setting up next to get the most out of it:

  • Set up GitLab Runners to enable CI/CD pipelines. You can run a Runner on the same VPS or a separate machine. The GitLab Runner Docker installation docs are the best starting point.
  • Enable the container registry to host your Docker images alongside your code. Add registry_external_url 'https://registry.yourdomain.com' to your Omnibus config and point a DNS record at your VPS.
  • Configure automated backups. GitLab’s backup command (gitlab-backup create) can be scheduled via cron. Pipe those backups offsite to an S3-compatible bucket for real peace of mind.
  • Tighten up access controls. Disable public sign-ups, configure two-factor authentication requirements for all users, and review the GitLab security hardening docs.
  • Set up monitoring. GitLab ships with a built-in Prometheus endpoint. Wire it up to a Grafana dashboard and you’ll always know how your instance is performing.

Running your own Git server used to mean serious operational overhead. GitLab on Docker cuts that down considerably. You’ve got a full-featured platform — version control, CI/CD, a container registry, issue tracking — all under your control, on hardware you understand. Scale it as your team grows, back it up on your schedule, and never worry about someone else’s pricing changes or uptime SLAs again.


Suggested SEO Metadata

Title: How to Install GitLab on a VPS with Docker: A Complete Guide

URL Slug: how-to-install-gitlab-on-a-vps-with-docker

Meta Description: Learn how to self-host GitLab CE on a VPS using Docker and Docker Compose. This step-by-step guide covers installation, Nginx reverse proxy setup, SSL with Let’s Encrypt, SSH access, updates, and troubleshooting.

The post How to Install GitLab on a VPS appeared first on GreenGeeks.

版权声明:
作者:玉兰
链接:https://www.techfm.club/p/236961.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>