claude-home/server-configs/gitea
Cal Corum 43b7440030 Update Gitea docs for shared actions and runner config
- Document DEFAULT_ACTIONS_URL=self and REQUIRE_SIGNIN_VIEW=false
- Correct runner setup: internal URL, config.yaml mount, no .netrc
- Add shared composite actions table (calver, gitea-tag, discord-notify)
- Document action reference rules (short form local, full URL GitHub)
- Add auth troubleshooting entry
- Update reference implementations with all 5 projects
- Replace semver references with CalVer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:09:08 -06:00
..
workflow-templates Optimize CLAUDE.md and fix Gitea Actions Docker cache 2026-02-13 15:14:35 -06: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 Add Gitea Actions workflow templates and automation 2026-02-05 13:40:17 -06:00
harbor-registry-setup.md Add Gitea Actions workflow templates and automation 2026-02-05 13:40:17 -06:00
INDEX.md Update Gitea docs for shared actions and runner config 2026-02-18 14:09:08 -06:00
README.md Update Gitea docs for shared actions and runner config 2026-02-18 14:09:08 -06:00

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: