# Setting Up a Private Gitea Instance on GCP

Managing your own Git server gives full control over repositories while keeping access private. In this post, I document the process of setting up **Gitea** on a **Google Cloud Platform (GCP) e2-micro VM**, including the problems encountered and how they were resolved.

> *Written with assistance from ChatGPT.*

Checkout my instance: &lt;https://gitea.9th.fun/&gt;

## **1\. Setting Up the VM**

I started with GCP’s **Free Tier e2-micro VM**:

* 1 non-preemptible e2-micro VM in a US region
    
* 30 GB persistent disk
    
* 1 GB outbound data transfer
    

**Firewall setup**:

* Allowed HTTP (80) and HTTPS (443) traffic
    
* SSH left open for private access
    

**Static IP**:

* Reserved a static IP for consistent domain mapping
    

## **2\. Preparing the VM**

Before connecting to the VM, generate a **SSH key pair** for secure authentication.

### **2.1: Generate an SSH key pair**

On **Windows** (PowerShell or Git Bash):

```powershell
# Create a folder for keys
mkdir C:\Users\<YOUR_USER>\Keys\gitea

# Generate an ed25519 SSH key pair
ssh-keygen -t ed25519 -C "USERNAME@gitea-vm" -f C:\Users\<YOUR_USER>\Keys\gitea\id_ed25519
```

* Optional: Enter a passphrase for extra security.
    
* This generates:
    

```powershell
C:\Users\<YOUR_USER>\Keys\gitea\id_ed25519       # Private key (keep secure!)
C:\Users\<YOUR_USER>\Keys\gitea\id_ed25519.pub   # Public key (upload to VM)
```

### **2.2: Add the public key to the VM**

* During VM creation, paste the public key in the **SSH keys section** of the GCP Console.
    
* Alternatively, after VM creation:
    

```bash
# On VM, create SSH directory for the user
mkdir -p ~/.ssh
echo "<paste-your-public-key-here>" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```

**Files involved**:

* `~/.ssh/authorized_keys` → Stores the public keys allowed to connect
    
* `~/.ssh/` → SSH directory
    

### **2.3: Test SSH connection**

On Windows:

```powershell
ssh -i C:\Users\<YOUR_USER>\Keys\gitea\id_ed25519 USERNAME@<VM_IP>
```

### **2.4: Update SSH config for convenience**

Edit `C:\Users\<YOUR_USER>\.ssh\config`:

```plaintext
Host gitea.example.com
  HostName <VM_IP>
  User USERNAME
  IdentityFile C:\Users\<YOUR_USER>\Keys\gitea\id_ed25519
  Port 22
```

## **3\. Installing Gitea**

### 3.1: Create necessary directories

```bash
sudo mkdir -p /var/lib/gitea/{custom,data,log}
sudo chown -R git:git /var/lib/gitea
```

**Directories involved**:

* `/var/lib/gitea/custom/` → Custom configs/templates
    
* `/var/lib/gitea/data/` → App data, database
    
* `/var/lib/gitea/log/` → Logs
    

### 3.2: Download Gitea binary

```bash
wget -O /usr/local/bin/gitea https://dl.gitea.io/gitea/1.21.0/gitea-1.21.0-linux-amd64
sudo chmod +x /usr/local/bin/gitea
```

**File involved**:

* `/usr/local/bin/gitea` → Main Gitea executable
    

### 3.3: Create a **systemd service** at `/etc/systemd/system/gitea.service`

```ini
[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target

[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea
Environment=USER=git HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea
ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini

[Install]
WantedBy=multi-user.target
```

## **4\. Nginx Reverse Proxy Setup**

### 4.1: Create a file `/etc/nginx/sites-available/gitea`

```nginx
server {
    listen 80;
    server_name gitea.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 90;
    }
}
```

### 4.2: Enable the site and reload Nginx

```bash
sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/gitea
sudo nginx -t
sudo systemctl reload nginx
```

**Files involved**:

* `/etc/nginx/sites-available/gitea` → Nginx config for Gitea
    
* `/etc/nginx/sites-enabled/gitea` → Symlink to enable site
    

## **5\. Initial Gitea Setup**

* Visit [`http://gitea.example.com`](http://gitea.example.com) to complete the installation.
    
* Use **SQLite** for simplicity.
    
* Create an **admin user**.
    

**Files involved**:

* `/etc/gitea/app.ini` → Main Gitea configuration
    
* `/var/lib/gitea/data/gitea.db` → SQLite database
    

## **6\. Enabling SSH for Gitea**

Update `/etc/gitea/app.ini`:

```ini
[server]
DISABLE_SSH = false
SSH_PORT = 2222
SSH_DOMAIN = gitea.example.com
START_SSH_SERVER = true
```

**Problem faced**:

* Ports `<1024` require root; Gitea runs as non-root.
    
* Service failed with: `Failed to start SSH server: listen tcp :22: bind: permission denied`.
    

**Solution**:

* Use a **non-privileged SSH port** (e.g., 2222).
    
* Restart Gitea to activate SSH:
    

```bash
sudo systemctl restart gitea
```

## **7\. Configuring Windows SSH**

Update `C:\Users\<YOUR_USER>\.ssh\config`:

```plaintext
Host gitea.example.com
  User git
  IdentityFile C:\Users\<YOUR_USER>\Keys\gitea\id_ed25519
  Port 2222
```

Test connection:

```powershell
ssh -v git@gitea.example.com
```

## **8\. Firewall & Networking**

* Open **TCP port 2222** in GCP firewall rules.
    
* Cloudflare only proxies HTTP/HTTPS → cannot proxy SSH.
    
* Use **VM public IP** for SSH pushes.
    

Example Git remote:

```powershell
git remote set-url gitea ssh://git@gitea.example.com:2222/USERNAME/<repo_name>.git
git push -u gitea master
```

## **9\. Final Notes**

* **Private Gitea**: Only admin can create accounts (`DISABLE_REGISTRATION = true`).
    
* **HTTP/HTTPS**: Served via Nginx, optionally protected by Cloudflare.
    
* **SSH for Git**: Non-privileged port (2222), keys registered in Gitea.
    
* **File paths modified/created**:
    

| File/Directory | Purpose |
| --- | --- |
| `C:\Users\<YOUR_USER>\Keys\gitea/id_ed25519*` | SSH keys |
| `~/.ssh/authorized_keys` | Public key storage |
| `C:\Users\<YOUR_USER>\.ssh/config` | SSH config for convenience |
| `/usr/local/bin/gitea` | Gitea executable |
| `/etc/systemd/system/gitea.service` | systemd service definition |
| `/var/lib/gitea/{custom,data,log}` | Gitea directories |
| `/etc/gitea/app.ini` | Gitea config |
| `/etc/nginx/sites-available/gitea` | Nginx config |
| `/etc/nginx/sites-enabled/gitea` | Nginx symlink |

---

> ✅ **Outcome**: Fully functional private Gitea instance on GCP:
> 
> * Web UI via [`https://gitea.example.com`](https://gitea.example.com)
>     
> * Git push/pull via SSH on port 2222
>     
> * Admin-only registration
>     
> * Secure key-based authentication
>
