Optimization

Deploy GitLab Self-Hosted on AlmaLinux VPS in Vietnam

You have a fresh AlmaLinux 9 VPS in Vietnam and you want to run your own GitLab instance for CI/CD. The default omnibus install works, but it ships with PostgreSQL, Redis, Puma, Sidekiq, and Nginx all on one box, which means on a 4 GB or even 8 GB RAM VPS it will run out of memory before you push your first commit. This guide walks through a GitLab self-hosted AlmaLinux VPS Vietnam deployment that is stable, secured behind nginx with HTTPS, and backed up on a schedule you can actually restore.

Key takeaways

  • Install GitLab CE 19.x from the official mirror, not from EPEL, so you stay on the supported release train.
  • Set prometheus_monitoring['enable'] = false and cap Puma workers to stop OOM kills on a 4 GB VPS.
  • Run nginx on the host as a reverse proxy so Let's Encrypt renewal is trivial and the bundled nginx stays disabled.
  • Back up with gitlab-backup create plus /etc/gitlab/gitlab.rb and the secrets file, then test a restore at least once.

Prerequisites

  • An AlmaLinux 9 VPS with at least 4 GB RAM and 2 vCPU. GitLab self-hosted officially needs 4 GB for small teams up to 100 users. On a 2 GB RAM VPS it will swap constantly, so size up.
  • Full root access over SSH. A non-root sudo user is fine, but this guide uses root for brevity.
  • A domain name like git.example.com pointing to the VPS public IP. Update the A record before you start.
  • Ports 80 and 443 open in the firewall. Also note GitLab SSH uses port 22 by default; if you changed the SSH port, set it in GitLab too.

If you plan to run a self-hosted GitLab VPS in Vietnam for a long-lived team, use a Linux VPS with NVMe storage. GitLab does constant random I/O on PostgreSQL and repository objects, and NVMe cuts clone and CI times noticeably.

Why run GitLab on AlmaLinux instead of Ubuntu

Most tutorials pick Ubuntu, but AlmaLinux 9 is a solid production choice. It is a 1:1 binary-compatible rebuild of RHEL 9, supported until May 2032 for security fixes. That means you get the same SELinux policy, firewalld, and dnf package management that enterprise shops use. For a GitLab self-hosted AlmaLinux VPS Vietnam deployment, the practical wins are SELinux enforcing by default, predictable upgrade cycles, and no surprise distro churn.

The trade-off is that some community scripts assume apt. You will use dnf for everything and the official GitLab repo, which is fully supported on AlmaLinux. If you later move to a dedicated server in Vietnam for larger teams, the same install path carries over without rework.

Step 1 - Update the system and open firewall ports

Start from a clean base. Update all packages and install the basic tooling you will need.

dnf update -y
dnf install -y curl wget policycoreutils-python-utils postfix

Postfix is optional, but GitLab sends notification emails and having a local MTA is easier than configuring SMTP later. If your VPS provider blocks port 25, skip postfix and configure GitLab SMTP against a mail relay instead.

AlmaLinux 9 uses firewalld by default. Add HTTP, HTTPS, and SSH explicitly, then reload.

firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload
firewall-cmd --list-all

Verify that the services list shows http, https, and ssh. If you run GitLab SSH on a non-standard port, open that port instead of ssh.

Step 2 - Install GitLab CE from the official repository

Add the GitLab package repository and install the Community Edition. As of 2026 the stable release train is GitLab 19.x, with patch releases like 19.2.5 and 19.3.1 shipping security fixes on a monthly cadence. Install the gitlab-ce package and it pulls the latest stable.

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash
dnf install -y gitlab-ce

This downloads roughly 1 GB of packages. On a VPS in Vietnam with domestic bandwidth of 100 Mbps on a 1 Gbps port, the download finishes in a couple of minutes. The install places everything under /opt/gitlab and registers a gitlab-runsvdir service that supervises all sub-services.

Verify the install version:

gitlab-rake gitlab:env:info | head -n 5

You should see the GitLab version and the revision. If this fails, the package may still be finishing its first reconfigure.

Step 3 - Configure GitLab for your domain and low RAM

The single config file is /etc/gitlab/gitlab.rb. Edit it and set the external URL first. This is the URL users will hit in the browser, so it must be the full HTTPS URL.

nano /etc/gitlab/gitlab.rb

Set these values. The commented lines show the defaults; you are overriding them.

external_url 'https://git.example.com'

# Reduce memory. GitLab ships several monitoring and profiling services you rarely need.
prometheus_monitoring['enable'] = false
grafana['enable'] = false
gitlab_rails['env'] = { 'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000' }

# Limit Puma workers. Rule of thumb: 2 workers per 4 GB RAM, 4 per 8 GB.
puma['worker_processes'] = 2
puma['min_threads'] = 4
puma['max_threads'] = 8

# Sidekiq gets its own cap.
sidekiq['max_concurrency'] = 10

# Use the system nginx later; disable the bundled one.
nginx['enable'] = false

Why the Puma numbers matter: each Puma worker reserves memory for Ruby heap and per-request objects. On a 4 GB VPS, two workers plus Sidekiq plus PostgreSQL and Redis sit near 3 GB total. Four workers push you past 4 GB and the kernel OOM killer starts taking out Sidekiq, which corrupts background jobs.

After editing, reconfigure GitLab. This step generates the database schema, starts all services, and can take 3 to 5 minutes.

gitlab-ctl reconfigure
gitlab-ctl status

The status output should list run: postgresql, run: redis, run: puma, and run: sidekiq among others. Anything in down state is a problem. Check logs with gitlab-ctl tail.

Step 4 - Set up nginx as a reverse proxy with HTTPS

GitLab bundles its own nginx, but running host nginx as a reverse proxy gives you one place to manage TLS for multiple services on the same VPS. Install nginx and configure a server block.

dnf install -y nginx
systemctl enable --now nginx

Create the server config. Use a real server block that forwards to GitLab Workhorse on port 8181.

nano /etc/nginx/conf.d/gitlab.conf
server {
    listen 80;
    server_name git.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name git.example.com;

    ssl_certificate     /etc/letsencrypt/live/git.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://127.0.0.1:8181;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Ssl on;
        proxy_redirect off;
    }
}

Certbot has an nginx plugin that obtains the certificate and reloads nginx automatically. Install it and run it once.

dnf install -y certbot python3-certbot-nginx
certbot --nginx -d git.example.com

This should print a success message and configure automatic renewal. Verify the nginx config and the site respond correctly.

nginx -t
curl -I https://git.example.com/users/sign_in

The curl output should show HTTP/2 200 or a 302 redirect to the login page. If you get a 502, GitLab Workhorse is not listening on 8181; check ss -tlnp | grep 8181.

Walk through the initial login screen, set the root password, and create your first project. GitLab self-hosted on an AlmaLinux VPS in Vietnam is now reachable over HTTPS.

Step 5 - Tune swap for GitLab on a low-memory VPS

Even with Puma capped, GitLab touches close to 3 GB on a 4 GB box. Add a swap file so background jobs do not trigger the OOM killer during spikes. AlmaLinux ships a swap partition by default on many VPS images, but check first.

free -h
swapon --show

If swap is empty or missing, create a 2 GB swapfile.

fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

Also lower the swappiness so the kernel prefers to keep hot pages in RAM and only spills cold ones.

sysctl vm.swappiness=10
echo 'vm.swappiness=10' >> /etc/sysctl.d/99-gitlab.conf

Verify swap is active: free -h should show Swap: 2.0Gi and swapon --show lists the file. A self-hosted GitLab VPS that swaps occasionally is fine; one that swaps constantly means either the RAM is too small or you left Prometheus enabled.

Step 6 - Back up GitLab and verify the restore path

GitLab stores everything in PostgreSQL plus repository files on disk. A consistent backup needs both the database dump and the configuration and secrets. The built-in tool does the database part; you copy the rest.

Create a cron job that runs the backup nightly and keeps the last 7 days, then syncs to a second location, ideally object storage or a different server.

crontab -e
0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1 SKIP=artifacts 2>&1 | logger -t gitlab-backup

Set retention in /etc/gitlab/gitlab.rb so old backups do not fill the disk:

gitlab_rails['backup_keep_time'] = 604800
gitlab_rails['backup_path'] = '/var/opt/gitlab/backups'

Run gitlab-ctl reconfigure after editing. Then copy the backup file plus /etc/gitlab/gitlab.rb and /etc/gitlab/gitlab-secrets.json off-box. Without the secrets file, a restore will produce an instance where existing CI variables and runner tokens cannot be decrypted.

The critical step most people skip: test the restore. On a throwaway VPS or a Docker container, extract the backup and run gitlab-ctl reconfigure then verify a project's files and CI variables are intact. A backup you never restored is a hope, not a plan. For a production team, pair GitLab backups with a GitLab VPS plan that includes snapshot and backup tooling so you have both nightly dumps and point-in-time disk snapshots.

Troubleshooting common GitLab failures on AlmaLinux

GitLab shows a 502 after reconfigure

Workhorse is not up. Run gitlab-ctl status and look for run: workhorse. If it is down, check the log for the real error rather than guessing.

gitlab-ctl tail workhorse

The usual cause on a fresh install is nginx on the host binding port 80 before the bundled nginx was fully disabled, or a stale socket at /var/opt/gitlab/gitlab-workhorse/socket. Restart everything: gitlab-ctl restart.

Git clone over SSH fails

If you run an SSH port other than 22, GitLab must know. Set gitlab_rails['gitlab_shell_ssh_port'] = 2222 in gitlab.rb and reconfigure. Also make sure the firewall allows the new port.

Out of memory during large pushes

Git receives the whole push in memory on the server. Sidekiq then runs hooks and CI triggers. If the box OOMs during a push, reduce Puma workers to 1 and set sidekiq['max_concurrency'] = 5. Then watch free -h during a heavy push to see the actual peak.

FAQ

How much RAM does a GitLab self-hosted VPS need in 2026?

GitLab officially requires 4 GB RAM for up to 100 users, and 8 GB for up to 1,000. On a 4 GB VPS you must disable Prometheus and cap Puma workers. A 2 GB VPS is not workable for GitLab regardless of tuning.

Can I use the bundled nginx instead of a reverse proxy?

Yes. Set external_url 'https://git.example.com', keep nginx['enable'] = true by default, and run certbot with the standalone plugin or webroot. The reverse proxy approach in this guide is for teams that host other services on the same VPS and want one TLS manager.

How do I update GitLab CE on AlmaLinux?

Run dnf update gitlab-ce then gitlab-ctl reconfigure. The package handles the upgrade. Always take a backup and a snapshot before major upgrades, and read the upgrade path if you jump more than one minor version.

Is AlmaLinux better than Ubuntu for running GitLab?

Neither is objectively better. Ubuntu is more commonly documented, but AlmaLinux gives you SELinux enforcing, firewalld, and a RHEL-compatible base that many enterprises standardize on. Choose based on what your team already runs, not on tutorials.

Where does GitLab store its data on AlmaLinux?

Everything lives under /var/opt/gitlab: repositories under git-data, PostgreSQL under postgresql/data, and backups under backups. Configuration is in /etc/gitlab/gitlab.rb. If you need more disk later, move git_data_dirs to a mounted volume after stopping GitLab.

Related articles

越南 VPS 提供本地 IPv4,适合部署面向本地团队的 GitLab 服务。

A Vietnam VPS gives you a local IPv4 that suits hosting GitLab for a team inside the country.

AlmaLinux VPS 部署 GitLab 要点

在越南的 AlmaLinux 9 VPS 上自建 GitLab,建议至少 4GB 内存,安装官方 gitlab-ce 包并关闭 Prometheus,把 Puma 限制在两个 worker 以内。用系统 nginx 做反向代理,结合 Certbot 自动续期 HTTPS 证书。每天用 gitlab-backup 备份数据库,同时备份 gitlab.rb 和 gitlab-secrets.json,并实际演练一次恢复流程,避免灾难发生时无法还原。

Note: This guide is for general reference. Every system and infrastructure has its own specifics, so test each step in a safe environment and consult a qualified engineer before applying it in production.