Installing Gitea on a VPS: Lightweight self-hosted Git server with Docker and SSL
TL;DR
In this guide, we will set up Gitea — a lightweight and powerful self-hosted Git server — on your VPS, using Docker and automatic SSL certificate acquisition via Caddy. You will get a fully functional platform for managing repositories, accessible via a domain name with HTTPS, ready for team development or personal projects.
- Set up a modern VPS based on Ubuntu 24.04 LTS with basic security.
- Install Docker and Docker Compose for easy application management.
- Deploy Gitea in a Docker container, using persistent volumes for data.
- Configure Caddy as a reverse proxy for Gitea, automatically providing HTTPS with Let's Encrypt.
- Prepare a backup system to protect your data.
- Gain control over your own Git infrastructure, completely independent of third-party services.
What We Are Setting Up and Why
In the modern development world, Git has become the de facto standard for version control. However, relying on third-party services like GitHub, GitLab.com, or Bitbucket is not always optimal. Many teams and individual developers need their own, fully controlled Git server. Gitea solves precisely this problem.
Gitea is a lightweight, open-source, and self-hosted Git service written in Go. It provides full functionality similar to GitHub or GitLab, but consumes significantly fewer resources, making it an ideal choice for deployment on a VPS or even a mini-computer like a Raspberry Pi. You will get an intuitive web interface for managing repositories, users, teams, pull requests, issue tracking, and much more.
What the Reader Will Get in the End
After completing this guide, you will have a fully configured and operational Gitea Git server, accessible via your domain name with a secure HTTPS connection. You will be able to create public and private repositories, invite colleagues for collaboration, manage access, and use all familiar Git operations via the web interface or command line. Your infrastructure will be entirely under your control, ensuring confidentiality and independence.
What Alternatives Exist and Why Self-Hosted on a VPS
There are several main approaches to hosting Git repositories:
- Cloud-managed services (GitHub, GitLab.com, Bitbucket): This is the simplest option in terms of maintenance. You simply register and start working. However, you depend on the provider's policy and infrastructure, may encounter limitations of free plans, and also face data privacy concerns as data is stored on third-party servers.
- Self-hosted GitLab Community Edition (CE): GitLab CE is a powerful solution offering not only Git hosting but also a complete DevOps cycle: CI/CD, container registry, monitoring, and much more. However, GitLab CE requires significantly more resources (minimum 4 GB RAM for comfortable operation), making it less suitable for small VPS and teams.
- Self-hosted Gitea: Gitea occupies a niche between simple cloud services and resource-intensive GitLab. It offers rich Git hosting functionality with minimal resource consumption. This is an ideal choice if you need a full-fledged Git server without unnecessary features and without large infrastructure costs. Deployment on a VPS gives you full control over your data, the ability to customize, and integrate with your own ecosystem. Additionally, Docker significantly simplifies Gitea deployment and management, isolating it from the main system and making it portable.
Choosing Gitea on a VPS allows combining the advantages of self-hosting (control, privacy) with efficient resource utilization and ease of deployment thanks to Docker.
What VPS Configuration is Needed for This Task
Installing Gitea on a VPS with Docker and Caddy generally does not require extremely powerful resources. Gitea is optimized to run on modest hardware, but the presence of Docker and Caddy adds a small overhead. It's important to consider how many users will actively work with Gitea and how many repositories are planned to be hosted.
Minimum Requirements
- CPU: 1 vCore (e.g., Intel Xeon E3/E5 or AMD EPYC). This will be sufficient for small teams of up to 5-10 people.
- RAM: 2 GB. This will be enough for the operating system, Docker daemon, Gitea, and Caddy containers. If very active use or other services are planned on the same VPS, it's better to consider 4 GB.
- Disk: 40-60 GB SSD. SSD is critical for Git operation performance. 40 GB will be enough for the OS, Docker images, and several dozen repositories. If repositories will be very large or numerous, consider 80-100 GB.
- Network: 100 Mbps. For most scenarios, this is more than sufficient. Channel stability and low latency are more important.
Specific VPS Plan for the Task (current as of 2026)
For comfortable operation of Gitea with Docker and Caddy for a team of up to 10-20 developers and dozens of repositories, the following VPS configuration is recommended:
- CPU: 2 vCore (e.g., Intel Xeon E5-2690v4 or AMD EPYC 7002 series).
- RAM: 4 GB DDR4.
- Disk: 80 GB NVMe SSD. NVMe will provide maximum disk operation speed, which is important for Git.
- Network: 1 Gbps port with unlimited or high volume traffic.
Such a VPS configuration will provide excellent performance and future headroom. For example, you can get a VPS with the specified characteristics.
When a Dedicated Server is Needed, Not a VPS
A dedicated server should be considered if:
- Very large team: More than 50-100 active users.
- Extremely large repositories: Repositories with gigabytes of binary files (LFS).
- Critical performance requirements: High load on Git operations 24/7.
- Strict security/isolation requirements: Complete isolation from "neighbors" on the hypervisor.
- Many other services are planned: In addition to Gitea, CI/CD agents, databases, web servers for other projects, etc., will run on the server.
For most Gitea tasks, especially for startups, small teams, and personal projects, a VPS will be an optimal and cost-effective solution.
Location: What It Affects
The choice of VPS location matters for:
- Latency: The closer the server is to your team and main users, the lower the latency will be when performing Git operations and working with the web interface. This is especially critical for developers located far from the server.
- Legal Compliance: In some cases, especially for companies, there may be requirements for data storage in a specific jurisdiction.
- Availability: Choose a location with reliable network infrastructure and good communication channels.
It is optimal to choose a data center located geographically close to most of your users.
Server Preparation
Before proceeding with Gitea installation, you need to perform basic configuration of your VPS. We will use Ubuntu Server 24.04 LTS, as it is a current and stable version of the operating system as of 2026.
Minimum Configuration After Provisioning
After gaining access to your VPS (usually via SSH as the root user), first update the system and create a new user with sudo privileges.
# Update package list and upgrade them to the latest versions
sudo apt update && sudo apt upgrade -y
# Create a new user (replace 'your_user' with the desired name)
sudo adduser ваш_пользователь
# Add the user to the sudo group to get administrative privileges
sudo usermod -aG sudo ваш_пользователь
Exit the root session and log in as the new user:
exit
# Then log in again with your_user
ssh ваш_пользователь@ваш_ip_сервера
SSH Key Setup (Recommended)
For increased security, it is recommended to use SSH keys instead of passwords. Generate a key on your local machine (if you don't have one already):
# On your local machine
ssh-keygen -t ed25519 -C "ваш[email protected]"
Copy the public key to the server:
# On your local machine
ssh-copy-id ваш_пользователь@ваш_ip_сервера
After this, disable password login for SSH (file /etc/ssh/sshd_config):
# On the server
sudo nano /etc/ssh/sshd_config
Find and change the following lines:
# /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
PermitRootLogin no
Restart the SSH server:
sudo systemctl restart sshd
Install Fail2Ban
Fail2Ban protects your server from brute-force attacks by blocking IP addresses from which numerous failed login attempts occur.
# Install Fail2Ban
sudo apt install fail2ban -y
# Copy the default config for customization
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Open the file for editing (optional, for finer tuning)
sudo nano /etc/fail2ban/jail.local
Default settings are usually sufficient, but you can change bantime, findtime, and maxretry. Make sure the [sshd] section is enabled.
# Example jail.local content (make sure enabled = true)
[DEFAULT]
bantime = 1d
findtime = 10m
maxretry = 5
[sshd]
enabled = true
mode = aggressive
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
# Restart Fail2Ban to apply changes
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
Firewall Configuration (UFW)
Uncomplicated Firewall (UFW) is an easy-to-use interface for iptables. We will configure it to allow only the necessary ports.
# Install UFW
sudo apt install ufw -y
# Allow SSH (default port 22)
sudo ufw allow ssh
# Allow HTTP (port 80) and HTTPS (port 443) for the web server (Caddy)
sudo ufw allow http
sudo ufw allow https
# Enable UFW
sudo ufw enable
Confirm enabling by pressing y. You can check the firewall status with the command sudo ufw status verbose.
Now your server is ready for Docker and Gitea installation.
Software Installation — Step-by-Step
We will use Docker and Docker Compose to deploy Gitea. This ensures application isolation, simplifies dependency management, and facilitates updates.
1. Install Docker Engine
To install Docker Engine on Ubuntu 24.04 LTS (Noble Numbat), follow the official recommendations, current as of 2026.
# Remove old Docker versions if they exist
for pkg in docker.io docker-doc docker-compose docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; do sudo apt remove $pkg; done
# Update package list
sudo apt update
# Install necessary packages to work with repositories
sudo apt install ca-certificates curl gnupg -y
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Add Docker repository to APT sources
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Update package list after adding Docker repository
sudo apt update
# Install Docker Engine, Docker CLI, containerd, and Docker Compose plugin
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
Verify Docker installation:
# Check Docker version
docker --version
# Check Docker service status
sudo systemctl status docker
# Run 'hello-world' test container
sudo docker run hello-world
Add your user to the docker group to avoid using sudo for every Docker command:
# Add user to docker group
sudo usermod -aG docker ваш_пользователь
# Apply group changes (log out and log back in or reboot the system)
newgrp docker
2. Create Directory Structure for Gitea
We will create directories to store Gitea data and Docker Compose configuration. This will ensure data persistence and a clean structure.
# Create root directory for Gitea project
mkdir -p ~/gitea
# Navigate to the created directory
cd ~/gitea
# Create directory for persistent Gitea data
mkdir -p ./data
3. Create docker-compose.yml file for Gitea and Caddy
This file will define the Gitea and Caddy services, their dependencies, volumes, and network settings. We will use SQLite as the database for simplicity, which is ideal for small installations. For larger projects, consider PostgreSQL or MySQL.
Create the file docker-compose.yml in the ~/gitea directory:
# Create docker-compose.yml file
nano docker-compose.yml
Paste the following content, replacing your.domain.com with your actual domain:
# docker-compose.yml
version: "3.8"
services:
gitea:
image: gitea/gitea:1.22.0 # Current version as of 2026, check https://dl.gitea.io/gitea/
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__DATABASE__DB_TYPE=sqlite3 # Using SQLite for simplicity
- GITEA__DATABASE__PATH=/data/gitea.db # Path to SQLite database file
- GITEA__SERVER__DOMAIN=your.domain.com # Your domain
- GITEA__SERVER__SSH_DOMAIN=your.domain.com # Domain for SSH access
- GITEA__SERVER__HTTP_PORT=3000 # Internal Gitea port
- GITEA__SERVER__APP_DATA_PATH=/data # Path for Gitea data inside the container
- GITEA__SERVER__ROOT_URL=https://your.domain.com/ # Base URL for Gitea
- GITEA__SECURITY__INSTALL_LOCK=true # Lock installation page after initial setup
- GITEA__SERVICE__DISABLE_REGISTRATION=false # Allow new user registration (true for a private server)
- GITEA__SERVICE__REQUIRE_SIGNIN_VIEW=false # Require sign-in to view repositories (true for a private server)
- GITEA__SESSION__PROVIDER=db # Using database for sessions
- GITEA__CACHE__ADAPTER=redis # Recommended for performance
- GITEA__CACHE__HOST=redis:6379 # Redis host
- GITEA__QUEUE__TYPE=redis # Task queue via Redis
- GITEA__QUEUE__CONN_STR=redis://redis:6379/0 # Redis connection string
restart: always
volumes:
- ./data:/data # Mapping local data directory to container
- /etc/timezone:/etc/timezone:ro # Timezone synchronization
- /etc/localtime:/etc/localtime:ro # Local time synchronization
ports:
- "3000:3000" # Internal Gitea HTTP port (for Caddy)
- "2222:22" # Gitea SSH port (mapping to 2222 to avoid conflict with host)
networks:
- gitea-network
caddy:
image: caddy:2.7.5 # Current Caddy version as of 2026
container_name: caddy
restart: always
ports:
- "80:80" # HTTP for Let's Encrypt challenge and redirection
- "443:443" # HTTPS
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile # Caddy configuration file
- ./caddy_data:/data # Caddy storage for certificates and state
environment:
- GITEA_DOMAIN=your.domain.com # Pass domain to Caddyfile via variable
networks:
- gitea-network
depends_on:
- gitea # Caddy depends on Gitea
redis:
image: redis:7.2-alpine # Lightweight Redis version
container_name: redis
restart: always
volumes:
- ./redis_data:/data # Redis data storage
networks:
- gitea-network
command: redis-server --appendonly yes # Enable data persistence
networks:
gitea-network:
driver: bridge
Important: Replace your.domain.com with your actual domain. Ensure that an A-record is created for your domain (e.g., gitea.example.com) pointing to your VPS's IP address.
Note on versions: Versions gitea/gitea:1.22.0, caddy:2.7.5, and redis:7.2-alpine are listed as current for 2026. Always check official Docker Hub repositories for the latest and most stable versions.
4. Create Caddyfile for Caddy
Caddy automatically manages SSL certificates via Let's Encrypt. Create the Caddyfile in the same ~/gitea directory:
# Create Caddyfile
nano Caddyfile
Paste the following content:
# Caddyfile
{env.GITEA_DOMAIN} {
# Automatic HTTPS
tls {
# Use Let's Encrypt
acme_challenge http
}
# Reverse proxy for Gitea
reverse_proxy gitea:3000 {
# Proxy headers required by Gitea
header_up Host {host}
header_up X-Real-IP {remote_ip}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote_ip}
}
# Settings for large files (Git LFS)
# Gitea handles LFS itself, but Caddy must pass large requests
# If you have very large LFS files, increasing body_size might be necessary
# (Caddy handles up to 10MB by default)
# For Gitea, this is usually not required, as it receives data itself
}
The {env.GITEA_DOMAIN} variable will be automatically replaced with the value from docker-compose.yml.
5. Start Gitea and Caddy with Docker Compose
After creating both files, you are ready to start Gitea.
# Navigate to the directory containing the files
cd ~/gitea
# Start all services in the background
docker compose up -d
This command will download the necessary Docker images (Gitea, Caddy, Redis), create the containers, and start them. The first run may take some time while the images are downloaded.
You can check the status of the running containers with the command:
# Check container status
docker compose ps
You should see that all containers (gitea, caddy, redis) are in running status.
At this stage, the installation of the main components is complete. Now let's move on to the final configuration.