Deploy GitLab CE on a Debian VPS in Vietnam: A Sysadmin's Guide

You have a small team, a handful of private repositories, and you are tired of hitting the monthly cap on a SaaS Git host. Or you are already on a Debian VPS in Vietnam for other workloads and want to keep your source code on local infrastructure with a dedicated IPv4. Either way, self-hosting GitLab CE is the standard move. This guide walks through a complete deployment of GitLab CE on Debian 12: package install, initial configuration, DNS, SSL via Let's Encrypt, a swap check, and automated backups. By the end you have a production-grade GitLab reachable over HTTPS on your own domain, with the data staying on a GitLab VPS sized for your team.
Prerequisites
Before you start, make sure the following is in place:
- A Debian 12 VPS with at least 4 GB of RAM. GitLab CE is memory-hungry; 2 GB barely runs it and the runner will starve. The official minimum is 4 GB.
- Root access or a sudo user on the server.
- A domain name pointing to the server's IPv4 address. You need an A record pointing to the VPS IP, and that IP should be a dedicated IPv4 in Vietnam to get clean domestic routing for your team.
- Ports 80 and 443 open in your firewall.
- Basic familiarity with systemd and the command line.
Why Run GitLab on Your Own VPS Instead of a SaaS
For a small team, a SaaS Git host works until it does not. The pain points are predictable: per-seat pricing climbs as you add contractors, private repos on free tiers are capped, and you have zero control over where your code lives. Self-hosted GitLab CE removes all three constraints at once. It is free, has no seat limits, and because it runs on a Linux VPS in your chosen datacenter, the data stays under your control.
There is also a latency angle that matters if your team sits in Vietnam. A Git server in Singapore or the US adds a round trip over international transit on every push and pull. Hosting GitLab on a Vietnam VPS keeps that traffic on the domestic backbone. Per the routing comparisons in 2026, domestic hops in Vietnam are fast and stable, so clone and push operations feel noticeably snappier for local developers.
Step 1 - Install GitLab CE on Debian 12
GitLab provides an official repository for Debian. The install is a simple apt flow, but it downloads a large package, so give it a couple of minutes.
sudo apt update
sudo apt install -y curl ca-certificates
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo apt install -y gitlab-ce
The script adds the GitLab repository and its signing key. The final command fetches the gitlab-ce package, which in 2026 is the current stable community edition. After it finishes, GitLab is installed but not yet configured.
Verify:
gitlab --version
The output shows the version string, e.g. gitlab-ce 17.x.x. If you see that, the package is in place.
Step 2 - Configure the External URL
GitLab needs to know the URL it will be served from. Edit the main configuration file and set the external_url directive to your domain:
sudo nano /etc/gitlab/gitlab.rb
Find the line that reads # external_url 'GENERATED_EXTERNAL_URL' and change it to your real domain. Use HTTPS directly so GitLab generates SSL-friendly defaults and redirects HTTP traffic:
external_url 'https://git.example.com'
Replace git.example.com with the domain you pointed to the server. If you want GitLab to listen on a non-standard port, you can append it, but for a standard HTTPS setup the default 443 is fine.
Now reconfigure GitLab to apply the change. This step also runs the internal setup and can take up to five minutes:
sudo gitlab-ctl reconfigure
The reconfigure step generates the nginx config, creates the database, and starts all the services. It ends with a gitlab Reconfigured! message.
Verify the service is up:
sudo gitlab-ctl status
Expected output lists run: nginx, run: postgresql, run: puma, run: sidekiq, all in the running state. If any shows down, check the logs with sudo gitlab-ctl tail <service>.
Step 3 - Secure It With Let's Encrypt
GitLab can request a Let's Encrypt certificate automatically. In gitlab.rb, look for the certificate settings and enable them:
letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['[email protected]']
letsencrypt['auto_renew'] = true
The contact_emails option is where Let's Encrypt sends expiry notices. Set it to a real inbox you check. The auto_renew directive schedules a daily renewal check, which keeps the certificate valid without manual intervention.
sudo gitlab-ctl reconfigure
During reconfigure, GitLab contacts Let's Encrypt, validates the domain, and installs the certificate. This only works if port 80 is reachable from the internet, since the HTTP-01 challenge needs to hit your server.
Verify the certificate:
echo | openssl s_client -connect git.example.com:443 2>/dev/null | openssl x509 -noout -dates
You should see a notAfter date roughly 90 days in the future. If instead you get a certificate for the wrong domain or a self-signed one, the challenge failed, so check that the A record resolves to this server and that port 80 is open.
Step 4 - Add Swap and Tune Memory on a 4 GB Instance
GitLab on a fresh 4 GB VPS tends to run close to the memory ceiling, especially when a runner starts a job. Add a swap file as a safety net:
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
The fstab entry ensures the swap is available after a reboot. The file is 4 GB, which doubles your effective memory headroom. On a 2GB RAM VPS this is not optional, it is survival. Even on 4 GB I keep it because puma and sidekiq both spike during a CI run.
You can also trim the puma worker count if the VPS is undersized. In gitlab.rb:
puma['worker_processes'] = 2
That limits puma to two workers, which reduces idle memory. Apply with sudo gitlab-ctl reconfigure.
Verify swap is active:
free -h
The swap line shows 4.0Gi total and 0B used at idle, which is the expected state. If the system ever gets tight, you see the used value climb instead of an OOM kill.
Step 5 - Create Users and a Project
With GitLab running, the first browser visit to https://git.example.com redirects to a password setup page. The root account is created during install; you set its password here, then log in as root.
Do not do daily work as root in GitLab. Create a real user for each developer:
- Go to Admin Area, then Users, and click New user.
- Fill in the name, email, and username. Leave the password fields empty so the user receives a reset link by email.
- Optionally add the user to a group and grant Developer or Maintainer access on the project.
For the repository itself, create it under a group rather than a personal namespace. Groups make it easier to manage permissions when the team grows past two people.
Verify from the command line: clone the empty project over HTTPS to confirm the flow works end to end.
git clone https://git.example.com/group/project.git
cd project
echo "# project" > README.md
git add README.md
git commit -m "initial commit"
git push -u origin main
A successful push means GitLab, the database, and the web server are all working together.
Step 6 - Automate Backups
GitLab stores everything, the databases, repositories, and uploads, in a way that only its own backup tool understands. Back up with gitlab-backup and also copy the secrets file, because restoring without /etc/gitlab/gitlab-secrets.json breaks 2FA and CI variables.
sudo gitlab-backup create
This creates a tar file in /var/opt/gitlab/backups named like 1700000000_2026_01_01_17.0.0_gitlab_backup.tar. The timestamp prefix matters, you use it to restore a specific backup later.
On its own this only fills the local disk. Copy the archive off the server with a cron job. An rsync to another machine or an S3-compatible bucket is the standard pattern:
rsync -avz /var/opt/gitlab/backups/ backup@offsite:/backups/gitlab/
Add both steps to a daily cron entry so the whole thing runs unattended:
sudo crontab -e
0 2 * * * /usr/bin/gitlab-backup create CRON=1
30 2 * * * rsync -avz /var/opt/gitlab/backups/ backup@offsite:/backups/gitlab/
The CRON=1 argument suppresses the progress output that would otherwise get emailed as noise. The backup runs at 02:00 and the copy at 02:30.
Verify the backup exists:
ls -lh /var/opt/gitlab/backups/
You should see a tar file with today's date, and its size should be a reasonable fraction of your repository data. A zero-byte tar means the backup failed silently, which is why the offsite copy matters.
Why Use a Vietnam Hosted IPv4 for GitLab
A self-hosted GitLab instance is only as good as its network path. If your developers are in Vietnam and the server sits in Singapore, every push crosses international transit, and the latency becomes visible in the UI, not just in git operations. A rent a VPS in Vietnam setup with a dedicated IPv4 keeps the traffic local. Pushes and pulls stay on domestic routes, which are stable and fast per the 2026 telecom routing data.
There is also a compliance angle. If your company operates under Vietnam's data localization rules, hosting source code and CI artifacts on a GitLab VPS inside the country keeps the data in-country by default. That is not the sole reason to self-host, but it removes a whole class of questions.
Troubleshooting
Three failures show up constantly in a GitLab deployment:
502 Bad Gateway after reconfigure. The web server is up but puma is not ready, usually because it is still booting or it crashed. Check with sudo gitlab-ctl status puma. If it is down, look at the logs:
sudo gitlab-ctl tail puma
Most of the time the fix is giving the machine more memory or lowering worker_processes.
Let's Encrypt fails with a connection error. Port 80 is likely closed in the firewall. GitLab's own nginx listens on it, but if the VPS firewall blocks inbound traffic, the challenge cannot reach your server. Open the port and rerun reconfigure.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo gitlab-ctl reconfigure
Git clone hangs after entering credentials. This is usually a Git version issue on the client. Git refuses older authentication methods over HTTPS. On your local machine, run:
git --version
If it is older than 2.30, update git. Alternatively, switch to SSH access via a deploy key, which avoids the credential helper entirely.
FAQ
How much RAM does GitLab CE really need on a Debian VPS?
The official minimum is 4 GB, and that is honest for a small team. With 4 GB, GitLab runs comfortably for up to about five active users with a runner. On a 2 GB VPS it will start, but puma and sidekiq will swap heavily and the UI will feel sluggish. Add a 4 GB swap file if you must run on 2 GB.
Can I run GitLab CE with Docker on Debian instead of the package install?
Yes, the gitlab/gitlab-ce image works well, but you trade the automatic systemd integration for container management. The package install on a Docker note: the package method is the least moving parts and the easiest to upgrade, which is why this guide uses it. Docker is the better choice if you already run everything else in containers.
How do I update GitLab CE after the initial install?
Updates come through the same apt repository. Run sudo apt update && sudo apt install gitlab-ce, then sudo gitlab-ctl reconfigure. Check the upgrade notes in each release for manual steps, some major versions require a specific migration. Back up before every upgrade.
What is the difference between GitLab CE and GitLab EE?
CE is the free community edition with the core features: repositories, issues, CI/CD, and code review. EE adds enterprise features like LDAP group sync, audit events, and advanced approval rules, and requires a license after a trial. Dedicated server users who need deep compliance controls often go EE; for most teams CE is enough.
Should I put GitLab behind a reverse proxy?
It works, but GitLab already ships with nginx configured. Adding your own proxy in front means you manage two web servers and need to pass the right headers, especially for WebSocket and git over HTTP. Only do it if you must unify SSL termination across many services on one IP.
Related articles
- Self host GitLab CE on Ubuntu VPS in Vietnam
- Deploy GitLab self hosted on AlmaLinux VPS in Vietnam
- Configure a GitLab runner for CI/CD on a Linux VPS
- Self-hosted GitLab CE minimum viable server spec
Debian VPS 部署 GitLab CE 指南
本文讲解了在 Debian 12 VPS 上安装和配置 GitLab CE 的完整步骤。建议分配至少 4GB 内存,开启交换分区,并使用 Let's Encrypt 配置 HTTPS。备份同样重要,需定期执行 gitlab-backup 并同步到异地。如果你在越南有团队,选择越南机房 VPS 和本地 IPv4 可降低推送与拉取延迟,同时满足数据本地化要求。


