claude-home/server-configs/gitea
Cal Corum 4b7eca8a46
All checks were successful
Reindex Knowledge Base / reindex (push) Successful in 3s
docs: add YAML frontmatter to all 151 markdown files
Adds title, description, type, domain, and tags frontmatter to every
doc for improved KB semantic search. The description field is prepended
to every search chunk, and domain/type/tags enable filtered queries.

Type values: context, guide, runbook, reference, troubleshooting
Domain values match directory structure (networking, docker, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 09:00:44 -05:00
..
workflow-templates docs: add YAML frontmatter to all 151 markdown files 2026-03-12 09:00:44 -05:00
.env.example Add Gitea self-hosted Git server (LXC 225) 2026-02-03 16:12:41 -06:00
apply_branch_protection.py Add Gitea Actions workflow templates and automation 2026-02-05 13:40:17 -06:00
deployment-strategies.md docs: add YAML frontmatter to all 151 markdown files 2026-03-12 09:00:44 -05:00
harbor-registry-setup.md docs: add YAML frontmatter to all 151 markdown files 2026-03-12 09:00:44 -05:00
INDEX.md docs: add YAML frontmatter to all 151 markdown files 2026-03-12 09:00:44 -05:00
README.md docs: add YAML frontmatter to all 151 markdown files 2026-03-12 09:00:44 -05:00

title description type domain tags
Gitea Server Setup and Config Complete setup and configuration reference for the self-hosted Gitea instance on LXC 225 (10.10.0.225). Covers service management, Gitea Actions CI/CD runner setup, shared composite actions, branch protection, backup/restore, and troubleshooting. reference server-configs
gitea
git
ci-cd
gitea-actions
lxc
postgresql
docker-runner
branch-protection

Gitea - Self-Hosted Git Server

LXC 225 | 10.10.0.225 | git.manticorum.com

Self-hosted Git server with web UI, Git LFS support, and Gitea Actions for CI/CD pipelines.

Quick Info

Property Value
Type LXC Container (Proxmox)
OS Ubuntu 20.04 LTS
IP 10.10.0.225
Public URL https://git.manticorum.com
Gitea Version 1.22.6
Database PostgreSQL 12
Reverse Proxy Nginx Proxy Manager (10.10.0.16)

Container Specs

  • VMID: 225
  • CPU: 2 cores
  • RAM: 2GB
  • Disk: 20GB
  • Features: Nesting enabled (for future Docker runner support)

Services

Gitea Web

  • Port: 3000 (internal)
  • Service: gitea.service
  • User: git
  • Work Dir: /var/lib/gitea
  • Config: /etc/gitea/app.ini
  • Data: /var/lib/gitea/data
  • Logs: /var/lib/gitea/log

PostgreSQL

  • Version: 12
  • Port: 5432 (localhost only)
  • Database: gitea
  • User: gitea
  • Service: postgresql

Management

Access Container

ssh root@10.10.0.225
# or via Proxmox
pct enter 225

Service Management

# Status
systemctl status gitea
systemctl status postgresql

# Restart
systemctl restart gitea

# Logs
journalctl -u gitea -f

Database Access

# As postgres user
sudo -u postgres psql -d gitea

# As gitea user (from container)
PGPASSWORD=gitea123 psql -U gitea -d gitea -h 127.0.0.1

Configuration

Main Config File

/etc/gitea/app.ini contains all Gitea settings:

  • Database connection
  • Server domain and URLs
  • SSH settings
  • LFS configuration
  • OAuth2/JWT secrets
  • Actions enabled

Permissions:

  • Owner: root:git
  • Mode: 640
  • Directory: 750 on /etc/gitea

Admin Account

  • Username: cal
  • Password: Set during initial setup (change immediately!)
  • Email: cal@manticorum.com

Features Enabled

  • Gitea Actions - Built-in CI/CD (GitHub Actions compatible)
  • Git LFS - Large file storage support
  • SSH Access - Git over SSH on port 22
  • Web UI - Repository browser and management
  • Organizations - Multi-user repository groups
  • Webhooks - Integration with external services

Key app.ini Settings (Actions)

[actions]
DEFAULT_ACTIONS_URL = self   # Short-form actions resolve against local Gitea instance

[service]
REQUIRE_SIGNIN_VIEW = false  # Public repos (like gitea-actions) cloneable without auth
  • DEFAULT_ACTIONS_URL=self means uses: cal/gitea-actions/calver@main resolves locally
  • REQUIRE_SIGNIN_VIEW=false only exposes PUBLIC repos — all private repos remain locked down
  • The only public repo is cal/gitea-actions (reusable CI composite actions, no sensitive code)

Backup

What to Backup

  1. PostgreSQL database: gitea database
  2. Repository data: /var/lib/gitea/data/gitea-repositories
  3. Configuration: /etc/gitea/app.ini
  4. Custom files: /var/lib/gitea/custom (if any)

Backup Commands

# Database dump
sudo -u postgres pg_dump gitea > gitea-backup-$(date +%Y%m%d).sql

# Full data directory
tar -czf gitea-data-$(date +%Y%m%d).tar.gz /var/lib/gitea

# Config only
cp /etc/gitea/app.ini gitea-app-$(date +%Y%m%d).ini

Restore

# Restore database
sudo -u postgres psql -d gitea < gitea-backup.sql

# Restore data
tar -xzf gitea-data.tar.gz -C /
chown -R git:git /var/lib/gitea

Upgrades

Upgrade Gitea

# Stop service
systemctl stop gitea

# Backup current binary
cp /usr/local/bin/gitea /usr/local/bin/gitea.backup

# Download new version
wget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/VERSION/gitea-VERSION-linux-amd64

# Set permissions
chmod +x /usr/local/bin/gitea

# Start service (will auto-migrate database)
systemctl start gitea

# Check logs
journalctl -u gitea -f

Check Version

/usr/local/bin/gitea --version

Setting Up CI/CD with Gitea Actions

Gitea Actions are enabled and the runner is deployed on the same LXC.

Runner Container

# Current production runner setup
docker run -d \
  --name gitea-runner \
  --restart unless-stopped \
  -e GITEA_RUNNER_REGISTRATION_TOKEN=<token-from-gitea-admin> \
  -e GITEA_INSTANCE_URL=http://10.10.0.225:3000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v gitea-runner-data:/data \
  -v /etc/gitea/runner-config.yaml:/config.yaml:ro \
  gitea/act_runner:latest

CRITICAL: GITEA_INSTANCE_URL must be the internal URL (http://10.10.0.225:3000), not the public domain. The runner uses this for API communication and auth token matching.

Runner Config (/etc/gitea/runner-config.yaml)

Key settings:

  • container.options: --add-host=git.manticorum.com:host-gateway — lets job containers resolve the public domain
  • container.force_pull: true — always pulls latest runner images
  • Labels: ubuntu-latest, ubuntu-22.04, ubuntu-20.04

Shared Composite Actions (cal/gitea-actions)

Reusable CI/CD actions shared across all projects. This repo is public (required for runner auth).

Action Purpose
cal/gitea-actions/calver@main Generate CalVer version (YYYY.MM.BUILD) from git tags
cal/gitea-actions/gitea-tag@main Create git tag via Gitea API
cal/gitea-actions/discord-notify@main Send Discord embed notification via webhook

Action Reference Rules

Because DEFAULT_ACTIONS_URL=self is set:

# Local actions (cal/gitea-actions) — use SHORT FORM
uses: cal/gitea-actions/calver@main

# GitHub actions — use FULL URL (otherwise they'd resolve against local Gitea)
uses: https://github.com/actions/checkout@v4
uses: https://github.com/docker/setup-buildx-action@v3
uses: https://github.com/docker/login-action@v3
uses: https://github.com/docker/build-push-action@v5

Creating a New Workflow

  1. Copy an existing workflow as a template (e.g., from major-domo-database)
  2. Use short form for cal/gitea-actions/* references
  3. Use https://github.com/ prefix for all GitHub action references
  4. Required secrets: DOCKERHUB_USERNAME, DOCKERHUB_TOKEN, DISCORD_WEBHOOK

Adding Repositories

Via Web UI

  1. Go to https://git.manticorum.com
  2. Click "+" → "New Repository"
  3. Fill in details and create

Via Command Line

# Add remote
git remote add homelab git@git.manticorum.com:cal/repo-name.git

# Or HTTPS
git remote add homelab https://git.manticorum.com/cal/repo-name.git

# Push
git push homelab main

Migrate from GitHub

Gitea has built-in migration:

  1. New Repository → "Migrate from GitHub"
  2. Enter GitHub URL and token
  3. Gitea will clone all commits, branches, tags

Integration with NPM

Reverse proxy is configured on NPM (10.10.0.16):

  • Domain: git.manticorum.com
  • Forward to: 10.10.0.225:3000
  • SSL: Let's Encrypt
  • Websockets: Enabled

Troubleshooting

Gitea won't start

# Check logs
journalctl -u gitea -n 50

# Common issues:
# - Permission on /etc/gitea/app.ini (should be 640, root:git)
# - PostgreSQL not running
# - Port 3000 already in use

Can't connect to database

# Check PostgreSQL is running
systemctl status postgresql

# Test connection
PGPASSWORD=gitea123 psql -U gitea -d gitea -h 127.0.0.1 -c "SELECT 1;"

# Check pg_hba.conf allows md5 auth
cat /etc/postgresql/12/main/pg_hba.conf | grep md5

502 Bad Gateway on web

# Check Gitea is listening
ss -tlnp | grep 3000

# Check NPM can reach container
curl http://10.10.0.225:3000

# Verify firewall rules (should allow from 10.10.0.0/24)

Actions runner not working

  • Ensure runner is registered in Gitea Admin → Actions → Runners
  • Check runner logs: docker logs gitea-runner
  • Verify GITEA_INSTANCE_URL uses internal URL (http://10.10.0.225:3000), NOT the public domain
  • Ensure runner has network access to Gitea

Actions can't clone composite actions ("authentication required")

  • Verify REQUIRE_SIGNIN_VIEW = false in app.ini (allows public repo clone without auth)
  • Verify DEFAULT_ACTIONS_URL = self in app.ini
  • Use short-form references for local actions (cal/gitea-actions/calver@main)
  • Use full GitHub URLs for GitHub actions (https://github.com/actions/checkout@v4)
  • The cal/gitea-actions repo must be set to public visibility

Security Notes

  • Database password is stored in /etc/gitea/app.ini (secured with 640 permissions)
  • SSH keys for Git access are stored per-user in Gitea database
  • JWT secrets are auto-generated and stored in config
  • LXC is unprivileged for better isolation
  • PostgreSQL only listens on localhost
  • REQUIRE_SIGNIN_VIEW=false — only public repos are accessible without login; all private repos remain fully protected
  • The only public repo is cal/gitea-actions (audited — contains no secrets or sensitive code)

Deployment Date

Created: 2026-02-03 By: Claude Code (Proxmox Skill) Initial Version: Gitea 1.22.6 on Ubuntu 20.04

Git Remotes

This repository is mirrored on both GitHub and Gitea for redundancy:


Branch Protection Manager

Automated script to apply consistent branch protection rules across all your Gitea repositories.

Features

  • Applies branch protection rules to all repositories for a user/organization
  • Supports dry-run mode to preview changes
  • Configurable protection rules via environment variables
  • Handles updating existing protection rules
  • Clear success/error reporting

Requirements

pip install requests

Configuration

The script (apply_branch_protection.py) uses these settings:

  • Disable direct pushes (force PR workflow)
  • Require 1 approval before merge
  • Restrict approvals to whitelisted users
  • Dismiss stale approvals when new commits are pushed
  • Enable status checks (for CI/CD)
  • Restrict merging to whitelisted users
  • Block merge on rejected reviews
  • Block merge if pull request is outdated (critical for DB migrations)

Usage

1. Create a Gitea API Token

  1. Go to https://git.manticorum.com/user/settings/applications
  2. Click "Generate New Token"
  3. Give it a name (e.g., "Branch Protection Script")
  4. Select permissions: repo (full control)
  5. Click "Generate Token"
  6. Copy the token (you won't see it again!)

2. Set Environment Variables

export GITEA_TOKEN='your-api-token-here'
export GITEA_URL='https://git.manticorum.com'  # Optional, defaults to this
export GITEA_OWNER='cal'  # Optional, defaults to cal

3. Run the Script

Dry run (preview changes without applying):

cd /mnt/NV2/Development/claude-home/server-configs/gitea
python apply_branch_protection.py --dry-run

Apply to all repositories:

python apply_branch_protection.py

Customizing Protection Rules

Edit the BranchProtectionConfig section in main() to customize the rules:

config = BranchProtectionConfig(
    branch_name="main",              # Branch to protect
    enable_push=False,               # Disable direct pushes
    required_approvals=1,            # Number of required approvals
    enable_approvals_whitelist=True, # Restrict who can approve
    approvals_whitelist_usernames=[GITEA_OWNER],
    dismiss_stale_approvals=True,    # Dismiss approvals on new commits
    enable_status_check=True,        # Require status checks to pass
    enable_merge_whitelist=True,     # Restrict who can merge
    merge_whitelist_usernames=[GITEA_OWNER],
    block_on_rejected_reviews=True,  # Block merge if reviews are rejected
    block_on_outdated_branch=True,   # Block merge if branch is outdated
    require_signed_commits=False,    # Require GPG signatures
)

Example Output

============================================================
Gitea Branch Protection Configuration
============================================================
Gitea URL: https://git.manticorum.com
Owner: cal
Branch: main
============================================================
Protection Rules:
  • Direct pushes: Disabled
  • Required approvals: 1
  • Approvals whitelist: cal
  • Dismiss stale approvals: True
  • Status checks enabled: True
  • Merge whitelist: cal
  • Block on rejected reviews: True
  • Block on outdated branch: True
  • Require signed commits: False
============================================================

Applying branch protection to 5 repositories...

📦 major-domo-database
   🔄 Updating existing protection...
   ✅ Successfully applied branch protection
📦 major-domo-bot
   ✅ Successfully applied branch protection
...

Troubleshooting Branch Protection Script

"GITEA_TOKEN environment variable is required"

  • You need to set your API token. See usage step 2 above.

"Error fetching repositories"

  • Check that your GITEA_URL is correct
  • Verify your API token has repo permissions
  • Ensure the GITEA_OWNER username is correct

"Could not delete existing protection"

  • The script will still try to create the new protection
  • If this fails, manually delete the old protection rule from Gitea web UI

"Error: 422 Unprocessable Entity" This usually means:

  • The branch doesn't exist in the repository
  • Invalid username in whitelist
  • Conflicting protection rule settings

API References

This script uses the Gitea API branch protection endpoints: