Skip to content 99% OFF 🎉 Anniversary Sale 99% OFF Shared Hosting Use Code HURRYUP Claim Offer 99% OFF Hosting
99% OFF Hosting — Code HURRYUP
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting cPanel Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Call +91 75795 45488
Login
Hosting Panel — cPanel & Billing Console Panel — VPS Management
ALL SYSTEMS OPERATIONAL
VPS

Self-Hosted Git Server on a VPS: Gitea Setup Guide

Getwebup 6 min read

If you're paying per-seat for private repos on GitHub or GitLab.com just to keep a few client projects out of public view, a self-hosted Git server on your own VPS solves that for the cost of a small droplet. Gitea is the tool most people reach for — it's a single Go binary, uses a fraction of the RAM GitLab needs, and gets you push/pull, issues, pull requests, and a web UI in about fifteen minutes.

Why Gitea instead of GitLab or Gogs

GitLab CE is powerful but heavy — realistically you want 4GB+ RAM just for it to start reliably, and upgrades between versions can be a whole afternoon. Gogs is where Gitea forked from, but the fork has pulled ahead in maintenance and features. For a VPS in the 1-2 vCPU / 2GB RAM range — which covers most small hosting plans — Gitea is the practical choice. It runs comfortably alongside a couple of small web apps on the same box.

What you need before you start

  • A VPS running Ubuntu 22.04/24.04 or AlmaLinux 9, with a non-root sudo user already set up
  • Docker and Docker Compose installed (docker compose version should return something)
  • A subdomain pointed at the VPS, e.g. git.yourdomain.com — an A record to the VPS's IP
  • Ports 80 and 443 open (443 for HTTPS, 80 for the Let's Encrypt challenge)

If Docker isn't installed yet: curl -fsSL https://get.docker.com | sh then add your user to the docker group with sudo usermod -aG docker $USER and log back in for it to take effect.

Step 1: Lay out the directories

mkdir -p ~/gitea/{data,config}
cd ~/gitea

Keeping data and config outside the container means an upgrade is just pulling a new image — nothing you care about lives inside the container itself.

Step 2: Write the Docker Compose file

cat > docker-compose.yml <<'EOF'
version: "3"
services:
  gitea:
    image: gitea/gitea:1.22
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__server__DOMAIN=git.yourdomain.com
      - GITEA__server__ROOT_URL=https://git.yourdomain.com/
      - GITEA__server__SSH_PORT=2222
    restart: unless-stopped
    volumes:
      - ./data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "127.0.0.1:3000:3000"
      - "2222:22"
EOF

Two things worth noting here. First, Gitea only binds to 127.0.0.1:3000 — it's never exposed directly to the internet, Nginx sits in front of it. Second, SSH runs on host port 2222 instead of 22, because your VPS's real SSH daemon is already using 22. Clone URLs will look like ssh://git@git.yourdomain.com:2222/user/repo.git — a minor inconvenience worth the clarity of not touching your system SSH config.

Bring it up:

docker compose up -d
docker compose logs -f gitea   # ctrl-C once you see it's listening

Step 3: Put Nginx in front of it with HTTPS

If you don't already have Nginx and Certbot on the VPS:

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

Create /etc/nginx/sites-available/gitea:

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

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
        client_max_body_size 512M;
    }
}

The client_max_body_size line matters more than it looks — the default 1MB limit will reject pushes with large binary blobs (design assets, video fixtures) with a cryptic 413 from Nginx rather than a Git error, which is confusing to debug later.

sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d git.yourdomain.com

Certbot rewrites the server block to redirect port 80 to 443 and installs the cert. Visit https://git.yourdomain.com and you should hit Gitea's install wizard.

Step 4: Finish the install wizard correctly

Gitea's setup page will ask for a database — for anything beyond a couple of users, pick SQLite only if you're testing; for real use, run a small Postgres or MySQL container alongside it and point Gitea at that instead. The fields that trip people up:

  • SSH Server Domain — set to git.yourdomain.com, same as the web domain
  • SSH Port — must match the host port you mapped (2222 in the example above), not 22
  • Gitea Base URL — must be the full HTTPS URL, not http, or generated clone URLs will be wrong

After the wizard, register your admin account immediately — Gitea allows open registration by default, and the first account created isn't automatically an admin unless you check the box on that first signup form.

Step 5: Lock down registration

Unless you're running a public Git host, disable open sign-ups. In ~/gitea/config/gitea/gitea.ini, under [service]:

DISABLE_REGISTRATION = true

Then docker compose restart gitea. From here, you invite users manually from the admin panel or they use the SSH keys you hand out — no public sign-up form sitting on the internet.

Clone, push, and day-to-day use

# over HTTPS
git clone https://git.yourdomain.com/yourname/project.git

# over SSH (note the custom port)
git clone ssh://git@git.yourdomain.com:2222/yourname/project.git

Add your public key under Settings → SSH Keys in the web UI, and pushes work exactly like GitHub. CI runners (GitHub Actions self-hosted, Drone, Woodpecker) can all point at this same server if you outgrow the built-in Gitea Actions.

Backups — don't skip this

Everything that matters lives in ~/gitea/data plus your database. A daily cron that snapshots both is enough for most setups:

#!/bin/bash
docker exec gitea gitea dump -c /data/gitea/conf/app.ini -f /data/backup-$(date +%F).zip
find ~/gitea/data -name "backup-*.zip" -mtime +7 -delete

Ship that zip somewhere off the VPS — object storage, another server, wherever your other backups go. A Git server with no backup is the one piece of infrastructure people always assume is "fine" right up until the disk that hosts it isn't.

Common problems

SymptomCauseFix
SSH clone hangs or refusesPort 2222 not open in the firewall, or SSH_PORT mismatch between compose file and gitea.iniOpen the port with UFW/firewalld and make sure both values match
Web UI loads but pushes over HTTPS get 502Container not actually listening, or Nginx pointed at the wrong portdocker compose logs gitea, confirm it's up, check proxy_pass target
Large repo push fails with 413Nginx client_max_body_size too lowRaise the limit in the server block and reload Nginx
Clone URL shows http instead of httpsROOT_URL set to http:// during installFix ROOT_URL in gitea.ini and restart the container

Prevention

Pin the Gitea image version in your compose file (as above) rather than tracking latest — Gitea does occasionally ship breaking config changes between major versions, and you want to read the release notes before you're mid-upgrade on a Friday. Keep the database and data directory backups running before you have real repos on the box, not after someone loses a week of commits because the disk filled up.

Frequently asked questions

Can I run Gitea without Docker, directly on the VPS?

Yes — Gitea ships as a single static binary you can run with a systemd service and a local MySQL/Postgres install. Docker just makes upgrades and backups more predictable, which is why most guides (including this one) lead with it.

How much VPS is enough to run Gitea for a small team?

1 vCPU and 1-2GB RAM handles a handful of users and moderate-sized repos comfortably. Bump to 2 vCPU / 4GB if you'll also run Gitea Actions (CI) on the same box, since builds are what actually eat CPU.

Is Gitea a real alternative to GitHub for private work, or just a toy?

It's used in production by plenty of small teams and even some larger organizations that want full control over their code. You lose GitHub's ecosystem (Copilot, Actions marketplace, Dependabot) but keep pull requests, issues, wikis, webhooks, and now built-in Actions.

Can I migrate existing GitHub repos into Gitea?

Yes — Gitea has a built-in 'Migrate Repository' option in the web UI that pulls directly from a GitHub/GitLab URL, including issues and PRs if you supply a personal access token. No local clone-and-push dance required.

Do I need a separate database, or is SQLite fine long-term?

SQLite works for solo use or light testing, but it doesn't handle concurrent writes well under real team load. For anything beyond one or two active users, run Postgres or MySQL as a second container from day one — migrating later is possible but adds an avoidable step.

#gitea #vps #git-server #self-hosted #nginx #ssh

Keep reading

Chat with Support