Self-Hosting GitLab on Your Own VPS: From Install to Working Email
If you've ever wanted your own private code repository — full control, no per-seat pricing, no third party holding your source — self-hosting GitLab is one of the most satisfying weekend projects you can take on. GitLab is built to be self-hosted, and the free Community Edition gives you almost everything the hosted service offers.
This guide walks through the whole journey: getting GitLab running on a VPS, logging in for the first time, creating accounts for your team, and finally getting email notifications working so the whole thing feels complete. I hit a few snags along the way, and I've folded the fixes into the steps so you can skip the trial and error.
Before You Start: Check Your Specs
GitLab is powerful, but it's not lightweight. Before anything else, make sure your server can handle it:
RAM: 4 GB is the realistic minimum, 8 GB is comfortable. It will technically boot on 2 GB, but it'll lean heavily on swap and feel sluggish.
CPU: 2 cores minimum, 4 recommended.
Disk: 10 GB or more free, plus room for however many repositories you plan to host.
OS: Ubuntu, Debian, AlmaLinux, RHEL, or openSUSE all work well.
If your server has less than 4 GB of RAM, seriously consider a lighter alternative like Gitea or Forgejo instead. They offer a similar Git-hosting experience and run happily on under 1 GB. But if you've got the resources, GitLab's feature set is hard to beat.
Step 1: Install the Dependencies
Start by updating your system and installing the packages GitLab needs:
sudo apt update
sudo apt install -y curl openssh-server ca-certificates tzdata perlIf you want email notifications later, install Postfix now too:
sudo apt install -y postfixDuring the Postfix install, a configuration dialog pops up asking for the "general type of mail configuration." Choose Internet Site — that's the standard option for a server that sends its own mail. It then asks for a "System mail name," which is just the domain that appears in outgoing email. You can accept the default hostname or enter something meaningful; it won't affect how you reach GitLab in the browser.
A heads-up worth internalizing early: if your server sits on a private IP address (like a home lab or internal network), outgoing mail from Postfix directly will usually get rejected by big providers like Gmail. We'll solve that properly in the email section by routing through a real SMTP provider — so don't stress about Postfix's own delivery.
Step 2: Add the GitLab Repository and Install
Pull in the official GitLab CE package repository:
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bashNow install GitLab, setting your external URL in the same command. This part matters more than it looks.
If your server has a real public domain, use HTTPS and GitLab will automatically fetch a free Let's Encrypt certificate:
sudo EXTERNAL_URL="https://gitlab.example.com" apt install gitlab-ceBut if you're running on a private IP or a LAN-only box, use plain HTTP and your IP address instead:
sudo EXTERNAL_URL="http://192.168.1.100" apt install gitlab-ceWhy the difference? If you set an https:// URL on a private IP, GitLab tries to get a Let's Encrypt certificate — and that fails, because Let's Encrypt can't validate an address it can't reach from the public internet. The install can hang or error out on that step. Starting with http:// sidesteps the whole problem. You can always add HTTPS later with a self-signed certificate or a local certificate authority.
The install itself takes several minutes. GitLab runs its configuration automatically at the end.
Step 3: First Login
GitLab generates a random root password during installation and stores it in a file. Read it with:
sudo cat /etc/gitlab/initial_root_passwordNote that the filename is literally initial_root_password — not a random string. Your login is:
Username:
rootPassword: the value on the
Password:line in that file
Open your GitLab URL in a browser, log in, and change the password immediately. That initial password file is automatically deleted 24 hours after install, so it's temporary by design.
If the Password File Is Gone
If more than a day has passed, or the file simply isn't there, you can reset the root password directly from the Rails console. This is a handy tool to know about — it's GitLab's built-in administrative shell.
Open it with:
sudo gitlab-rails consoleIt takes 30 seconds to a couple of minutes to load — it's normal for it to sit there quietly. When you see a prompt like irb(main):001:0>, you're in. Then run:
user = User.find_by(username: 'root')
user.password = 'YourNewPassword123!'
user.password_confirmation = 'YourNewPassword123!'
user.save!
exitA => true after save! means it worked. Pick a password with at least 8 characters, or the validation will reject it.
Step 4: Creating Accounts for Your Team
Once you're in as root, you'll want to add your developers. You have two broad approaches:
Manual account creation (simplest). As admin, create each account and set the password yourself in the admin panel, then hand out the credentials. Turn off self-registration and email confirmation so nobody gets stuck waiting on an email that isn't set up yet. You'll find these under Admin → Settings → General → Sign-up restrictions. This approach works with zero email configuration — perfect for getting a small team running today.
Self-registration (needs email). If you'd rather let developers sign up themselves, GitLab sends a confirmation link they must click to activate their account. Password resets and notifications also depend on email. That means you'll need working email first — which brings us to the final piece.
Step 5: Getting Email to Actually Work
The local Postfix install from Step 1 is fine for basics, but the reliable way to send email — especially from a private IP — is to route through a real SMTP provider. Gmail works well for testing.
Edit the main GitLab configuration file:
sudo nano /etc/gitlab/gitlab.rbAdd your SMTP settings. Here's a working Gmail example:
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.gmail.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "aghimire074@gmail.com"
gitlab_rails['smtp_password'] = "your-app-password"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['gitlab_email_from'] = "aghimire074@gmail.com"Two Gmail-specific notes. First, your-app-password is not your normal Gmail password — you generate a dedicated "app password" in your Google account's security settings (you'll need 2-factor authentication enabled first). Second, keep the from address the same as the account you authenticate with. Gmail rewrites or rejects mail claiming to be from an address it didn't authenticate, so mismatches cause silent failures.
Fixing the Reply-To Address
By default, GitLab sets the reply-to address to something like noreply@your-server-ip, which looks broken to recipients. Clean it up by adding these lines:
gitlab_rails['gitlab_email_reply_to'] = 'aghimire074@gmail.com'
gitlab_rails['gitlab_email_display_name'] = 'GitLab'Now replies go to a real address, and emails show a friendly sender name instead of a bare IP.
Apply and Test
Any time you edit gitlab.rb, you apply the changes with:
sudo gitlab-ctl reconfigureDon't worry about running this command — it's idempotent, meaning you can run it as many times as you like with no harm. It only regenerates config and restarts services as needed; it never touches your repositories, users, or data. The web interface may be briefly unreachable for 30–60 seconds while services restart, and that's the only impact.
Once it finishes, test your email from the Rails console:
sudo gitlab-rails consoleThen:
Notify.test_email('aghimire074@gmail.com', 'Test', 'Hello from GitLab').deliver_nowCheck your inbox. If the message arrives with a sensible from and reply-to address, your email pipeline is complete.
Wrapping Up
At this point you've got a fully functional, self-hosted GitLab: repositories under your control, admin access secured with your own password, accounts for your team, and email notifications flowing. From here you can explore CI/CD pipelines, container registries, and the dozens of other features baked into the Community Edition.
A couple of parting tips. Set up automated backups from day one with gitlab-backup create on a cron schedule — don't wait until you wish you had them. And if this ever grows beyond a hobby project, consider moving from a personal Gmail sender to a dedicated transactional email provider like SendGrid, Mailgun, or Amazon SES, which are built for reliable delivery at volume.
Self-hosting isn't for every project, but when you want ownership and control over your development infrastructure, few tools deliver as much as a GitLab instance that's entirely your own.
Written by
Abhishek Ghimire
Writes here about engineering, technology, and the things worth building.