# How to install GitLab CE on Ubuntu or Debian with built-in Let's Encrypt

> Install GitLab Community Edition from GitLab's official package repository with Let's Encrypt HTTPS, then lock down sign-ups, back it up and upgrade safely.

Difficulty: Intermediate\
Tested on: Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 13

GitLab Community Edition (CE) brings Git repositories, merge requests, issues, CI/CD pipelines and container and package registries together in one self-managed application. The official **Linux package** bundles GitLab together with PostgreSQL, Redis, Gitaly, Puma, Sidekiq and NGINX, and you configure all of it through a single file, `/etc/gitlab/gitlab.rb`.

This guide installs GitLab CE from GitLab's own package repository on Ubuntu or Debian, lets GitLab obtain a Let's Encrypt certificate by itself, secures the root account and sign-ups, and then covers backups, upgrades along GitLab's required upgrade stops, and the problems you are most likely to meet on a single server.

## Prerequisites

You need a server running a release that GitLab supports. In October 2026, when GitLab 19.4 is current, GitLab's supported platforms table shows:

| Operating system | Status in GitLab's supported platforms table |
|---|---|
| Ubuntu 26.04 LTS | Supported from GitLab 19.3.0 |
| Ubuntu 24.04 LTS | Supported |
| Debian 13 | Supported from GitLab 18.5.0 |
| Debian 12 | Proposed last supported GitLab version 19.3.0, so not recommended for new installations |

GitLab builds packages for amd64 and arm64 and notes known issues on ARM. The Ubuntu installation page itself still lists only 22.04 and 24.04; Ubuntu 26.04 support comes from the supported platforms table. You also need:

- A non-root user with `sudo` rights and SSH key login, as in [Secure a new Linux server](/guides/secure-a-new-linux-server) and [Set up SSH keys](/guides/ssh-keys). GitLab uses the server's own OpenSSH for Git over SSH as the `git` user; if you restricted logins with `AllowUsers` or `AllowGroups`, include `git`.
- A domain such as `gitlab.example.com` with an A (and AAAA) record pointing at the server. Let's Encrypt needs inbound HTTP and HTTPS access and a valid hostname.
- Ports 80 and 443 free: GitLab's bundled NGINX listens on them, so do not run another web server on this host.
- Outbound access to `https://packages.gitlab.com/` and `https://storage.googleapis.com/packages-ops/` if a firewall filters outgoing traffic.

| Resource | Minimum (official) | Suggested starting point |
|---|---|---|
| CPU | 8 vCPU baseline for a single node | 8 vCPU |
| Memory | 16 GB baseline; at least 8 GB with memory-constrained settings | 16 GB |
| Disk | 40 GB for the application, plus all repositories, plus 5–12 GB for PostgreSQL | 100 GB SSD, growing with your repositories and CI artifacts |

The minimum column comes from GitLab's installation requirements. The suggested column is a conservative starting point, not an official or benchmarked figure. GitLab's requirements also ask you to disable swap where possible, or to provide enough memory that GitLab never uses it.

## Step 1 — Open the firewall

GitLab's installation pages enable SSH and open SSH, HTTP and HTTPS in ufw. Port 22 also carries Git over SSH:

```bash
sudo systemctl enable --now ssh
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
```

Unlike Docker-based apps, GitLab's NGINX runs directly on the host, so ufw rules apply to it normally. If your SSH daemon listens on another port, allow that port instead of 22 before you enable ufw.

## Step 2 — Add GitLab's package repository

GitLab provides a script that configures its apt repository. Install `curl`, download the script, read it, then run it:

```bash
sudo apt update
sudo apt install -y curl
curl --location "https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh" -o gitlab-ce-repo.sh
less gitlab-ce-repo.sh
sudo bash gitlab-ce-repo.sh
```

The script:

- detects your distribution and codename from `/etc/os-release`;
- installs `apt-transport-https`, `gnupg` if `gpg` is missing, and on Debian `debian-archive-keyring`;
- downloads the repository definition for your release into `/etc/apt/sources.list.d/gitlab_gitlab-ce.list`;
- downloads GitLab's signing key, converts it and stores it as `/etc/apt/keyrings/gitlab_gitlab-ce-archive-keyring.gpg`;
- runs `apt-get update` and stops with an error if that fails.

> **Tip**
>
> The official one-line equivalent is `curl --location "https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh" | sudo bash`.

Check that apt now offers GitLab CE from GitLab's repository:

```bash
apt-cache policy gitlab-ce
```

The `Candidate` line shows the newest version, and the source is `packages.gitlab.com`.

## Step 3 — Install GitLab CE with HTTPS

Pass your public address in `EXTERNAL_URL`. Because it starts with `https://`, GitLab requests a Let's Encrypt certificate during the first configuration run:

```bash
sudo EXTERNAL_URL="https://gitlab.example.com" apt install gitlab-ce
```

The installation and the first `gitlab-ctl reconfigure` take several minutes. GitLab also accepts `GITLAB_ROOT_EMAIL` and `GITLAB_ROOT_PASSWORD` (at least 8 characters) on the same command line, but only on the first installation, and the password then ends up in your shell history; the generated password in Step 4 avoids that. If GitLab cannot detect a valid hostname, reconfigure does not run automatically; then pass the same variables to `sudo gitlab-ctl reconfigure`.

Check the services and the site:

```bash
sudo gitlab-ctl status
curl -I https://gitlab.example.com
```

Every service line starts with `run:`. `curl` returns a redirect to the sign-in page over a valid certificate. A `502` page in the first minute or two is normal while Puma starts.

## Step 4 — Sign in as root

GitLab generates a random root password and stores it for 24 hours:

```bash
sudo cat /etc/gitlab/initial_root_password
```

Open `https://gitlab.example.com`, sign in as `root` with that password, then immediately:

1. Set a new long password in your user settings, under **Password**.
2. Change the root account's email address to one you read.
3. Turn on two-factor authentication for the account.

After 24 hours GitLab deletes the file automatically. If you lose the password, the troubleshooting section shows how to reset it.

## Step 5 — Restrict sign-ups and set the certificate contact

By default, anyone who can reach your GitLab can request an account; new instances require administrator approval for these sign-ups. If only invited people should have accounts, switch sign-up off:

1. In the upper-right corner, select **Admin**.
2. In the left sidebar, select **Settings**, then **General**.
3. Expand **New user account restrictions**.
4. Clear **Allow new user accounts** and select **Save changes**.

Create accounts yourself under **Admin**, **Users** from now on.

Next, tell Let's Encrypt where to send expiry warnings. Open the configuration file:

```bash
sudo nano /etc/gitlab/gitlab.rb
```

Add or edit this line:

```conf
letsencrypt['contact_emails'] = ['admin@example.com']
```

Apply the change:

```bash
sudo gitlab-ctl reconfigure
```

GitLab renews the certificate on its own schedule (by default after midnight on every fourth day of the month); you never need to run Certbot.

GitLab emails account confirmations, password resets, invitations and notifications; send them through your relay by setting `gitlab_rails['smtp_enable'] = true`, `smtp_address` (`smtp.example.com`), `smtp_port` (`587`), `smtp_user_name`, `smtp_password` and `smtp_enable_starttls_auto = true` in the same `gitlab.rb`, as shown in GitLab's [SMTP settings](https://docs.gitlab.com/omnibus/settings/smtp/), then run `sudo gitlab-ctl reconfigure`.

> **Note**
>
> Outbound port 25 is closed by default on HyperDC VPS. For services bought for a term of 3 months or longer, it is opened on request: [open a support ticket](/guides/support-tickets). Until then, send mail through an SMTP relay on port 587.

## Step 6 — Tune GitLab for a smaller server (optional)

If your server has less than the 16 GB baseline, GitLab documents settings for memory-constrained environments. The most effective ones go into `/etc/gitlab/gitlab.rb`:

```conf
puma['worker_processes'] = 0
sidekiq['concurrency'] = 10
prometheus_monitoring['enable'] = false
```

Run `sudo gitlab-ctl reconfigure` afterwards. The first run with these settings can take a while. GitLab's page for memory-constrained environments also covers Gitaly concurrency limits and memory allocator settings if you need to go further.

## Back up and restore

A GitLab backup has two parts. The `gitlab-backup` tool archives the database, repositories and uploads into a `.tar` file in `/var/opt/gitlab/backups`. It does **not** include the configuration in `/etc/gitlab`, and especially not `gitlab-secrets.json`, which holds the keys that decrypt values stored in the database. Back up both:

```bash
sudo gitlab-backup create
sudo ls -lh /var/opt/gitlab/backups
sudo mkdir -p /opt/backups
sudo tar czf /opt/backups/gitlab-etc-$(date +%F).tar.gz /etc/gitlab
```

Keep the configuration archive somewhere other than the data backups, because together they give full access to your data. If the backup reports `file changed as we read it` on busy instances, use `sudo gitlab-backup create STRATEGY=copy`, which needs up to the same amount of disk space again.

To back up every night at 02:00, add this line to root's crontab with `sudo crontab -e`:

```text
0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1
```

Limit how long local archives are kept with `gitlab_rails['backup_keep_time'] = 604800` (seven days) in `gitlab.rb`. GitLab can also upload each archive to S3-compatible storage through the `backup_upload_connection` settings. Either way, copy backups off the server.

**Restore.** You can only restore into exactly the same GitLab version, installed from the same `gitlab-ce` package. Install that version, put your saved `/etc/gitlab/gitlab-secrets.json` and `gitlab.rb` back in place and run `sudo gitlab-ctl reconfigure`. Then copy the archive into the backup folder and restore it with Puma and Sidekiq stopped. Use your own archive name; the `BACKUP` value is the name without `_gitlab_backup.tar`.

> **Danger**
>
> `gitlab-backup restore` overwrites the contents of the GitLab database.

```bash
sudo cp 1791500000_2026_10_09_19.4.1-ce_gitlab_backup.tar /var/opt/gitlab/backups/
sudo chown git:git /var/opt/gitlab/backups/1791500000_2026_10_09_19.4.1-ce_gitlab_backup.tar
sudo gitlab-ctl stop puma
sudo gitlab-ctl stop sidekiq
sudo gitlab-ctl status
sudo gitlab-backup restore BACKUP=1791500000_2026_10_09_19.4.1-ce
sudo gitlab-ctl reconfigure
sudo gitlab-ctl start
sudo gitlab-rake gitlab:check SANITIZE=true
sudo gitlab-rake gitlab:doctor:secrets
```

The last command confirms that the restored secrets can decrypt the values in the database.

## Upgrade GitLab

GitLab releases a new minor version every month, and some versions are **required upgrade stops** that you must install before going further: 18.2, 18.5, 18.8 and 18.11 in GitLab 18, and 19.2, 19.5, 19.8 and 19.11 in GitLab 19 (later stops may not be released yet). GitLab's Upgrade Path tool lists the exact versions for your starting point. At each stop, wait until background migrations have finished before you move on.

The package takes an automatic database backup before it upgrades, but GitLab still asks you to keep a full, current backup of your own. A typical upgrade to a specific version looks like this:

```bash
sudo gitlab-backup create
sudo apt update
apt-cache madison gitlab-ce
sudo apt install gitlab-ce=19.4.1-ce.0
sudo gitlab-rake gitlab:background_migrations:list
```

`apt-cache madison` lists the versions in the repository; replace `19.4.1-ce.0` with your target. The last command (GitLab 18.9 and later) shows queued background migrations; you can also check them under **Admin**, **Monitoring**, **Background migrations**. While the upgrade runs, a single-node server shows a deploy message or a `502` page.

> **Warning**
>
> A plain `sudo apt upgrade` or `sudo apt install gitlab-ce` installs the newest version and can jump across required stops if you have fallen behind. To prevent that, put the package on hold with `sudo apt-mark hold gitlab-ce`, and release it with `sudo apt-mark unhold gitlab-ce` only when you upgrade deliberately.

## Troubleshooting

### 502 error right after installation, a restart or an upgrade

Puma is still starting; on a small server this can take a minute or more. Check `sudo gitlab-ctl status` and follow the logs with `sudo gitlab-ctl tail puma`. If the 502 persists, look at memory with `free -h`. GitLab Workhorse also returns 502 when a single request takes longer than one minute; for very slow pages you can raise `gitlab_workhorse['proxy_headers_timeout']` in `gitlab.rb`.

### Let's Encrypt fails during reconfigure

Let's Encrypt must reach the server on ports 80 and 443 under the name in `EXTERNAL_URL`. Check the DNS record, open both ports in ufw and in any provider firewall, then run `sudo gitlab-ctl reconfigure` again. You can force a renewal later with `sudo gitlab-ctl renew-le-certs`.

### Errno::ENOMEM: Cannot allocate memory during backup or upgrade

GitLab needs about 2 GB of available memory for these tasks. If the server already uses swap during normal operation, add memory; otherwise adding swap may be enough to get through the backup or upgrade.

### PostgreSQL reports that it could not create a shared memory segment

The bundled PostgreSQL tries to reserve 25% of the server's memory. Set `postgresql['shared_buffers'] = "100MB"` in `gitlab.rb` and run `sudo gitlab-ctl reconfigure`.

### reconfigure stops with undefined method for nil:NilClass

`gitlab.rb` contains an invalid or obsolete setting, often after an upgrade. Compare your file with the current template using `sudo gitlab-ctl diff-config`, fix the setting and run reconfigure again.

### The initial root password no longer works or the file is gone

The file is deleted after 24 hours. Reset the root password with GitLab's Rake task and enter a new password twice:

```bash
sudo gitlab-rake "gitlab:password:reset[root]"
```

## Next steps

- For a lighter Git server, compare [Forgejo or Gitea](/guides/install-gitea-forgejo).
- Deploy applications from your repositories with [Coolify](/guides/install-coolify) or [Dokploy](/guides/install-dokploy).
- Review servers sized for GitLab on the [GitLab hosting](/gitlab-hosting) page.
- Read the official [GitLab documentation](https://docs.gitlab.com/) for runners, email delivery and the container registry.

## Frequently asked questions

### Which Ubuntu and Debian versions does GitLab support?

In October 2026 GitLab’s supported platforms table lists Ubuntu 22.04, 24.04 and 26.04 (26.04 from GitLab 19.3) and Debian 11, 12 and 13. Debian 11 and 12 have a proposed last supported GitLab version of 19.3, so use Ubuntu 24.04, Ubuntu 26.04 or Debian 13 for a new server.

### How much memory does GitLab CE need?

GitLab’s requirements give 8 vCPU and 16 GB of memory as the baseline for a single-node installation. With the settings for memory-constrained environments, a single node can run with at least 8 GB.

### Where do I find the GitLab root password after installation?

Unless you set one during installation, GitLab writes a random password to /etc/gitlab/initial_root_password. The file is deleted automatically after 24 hours, so sign in as root and change the password right away.

### Does gitlab-backup include the GitLab configuration?

No. The backup archive does not contain /etc/gitlab. Back up /etc/gitlab/gitlab-secrets.json and /etc/gitlab/gitlab.rb separately, because a restore needs the same secrets file to decrypt values stored in the database.

### Can I upgrade GitLab straight to the newest version?

Only if no required upgrade stop lies between your version and the target. GitLab 19 has stops at 19.2, 19.5, 19.8 and 19.11; install each stop’s exact package version and let background migrations finish before you continue.

---

Source: <https://hyperdc.com/guides/tutorials/install-gitlab-ce>\
Updated: 2026-10-09
