Part 5 of 5

    Team Workflows & Production Hardening

    PR review workflows, CODEOWNERS, branch protection, project boards, and a complete security hardening checklist.

    This is Part 5 of the Gitea DevOps Pipeline Series. If you are starting here, Parts 1 through 4 cover installation, CI/CD, package registries, and auto-deployment. The settings in this guide build on a running Gitea instance and are independent of the PaaS integration.

    1

    Pull Request Review Workflows

    Configuring Required Reviews

    Navigate to Repository → Settings → Branches to configure branch protection:

    Branch protection settings
    # You configure these through the Gitea UI, not config files.
    # Repository > Settings > Branches > Add Branch Protection Rule
    
    # Rule for: main
    # Required approvals before merging: 1 (or more for larger teams)
    # Dismiss stale approvals when new commits are pushed: checked
    # Require review from code owners: checked (if using CODEOWNERS)
    # Block merge on rejected reviews: checked
    # Require status checks to pass: checked
    # Status checks: your CI workflow name (e.g., 'test')
    # Block force pushes: checked
    # Block deletion of protected branch: checked
    2

    CODEOWNERS File

    Gitea supports CODEOWNERS files, which automatically request reviews from specific users or teams when files they own are changed in a PR:

    .gitea/CODEOWNERS
    # Global rule: all files require review from the platform team
    * @your-org/platform-team
    
    # Infrastructure and CI files require DevOps review
    /.gitea/ @your-org/devops
    /docker-compose*.yml @your-org/devops
    /Dockerfile* @your-org/devops
    
    # API files require backend team review
    /src/api/ @your-org/backend
    
    # Frontend files require frontend team review
    /src/frontend/ @your-org/frontend
    
    # Security-sensitive files require security team
    /src/auth/ @alice @bob
    /.env.example @alice

    Note: The CODEOWNERS file only triggers review requests — it does not block merging unless combined with 'Require review from code owners' in branch protection.

    3

    PR & Issue Templates

    PR Template

    .gitea/pull_request_template.md
    ## Summary
    What does this PR do? (1-2 sentences)
    
    ## Changes
    - List the key changes
    - Be specific about what was added, removed, or modified
    
    ## Testing
    How was this tested?
    - [ ] Unit tests added or updated
    - [ ] Tested manually on staging
    - [ ] No test coverage needed (explain why)
    
    ## Checklist
    - [ ] Documentation updated
    - [ ] Environment variables documented in .env.example
    - [ ] No debug logging left in code
    
    ## Related Issues
    Closes #ISSUE_NUMBER

    Issue Template

    .gitea/ISSUE_TEMPLATE/bug_report.md
    ---
    name: Bug Report
    about: Report unexpected behavior
    labels: bug
    ---
    
    **Describe the bug**
    A clear description of what the bug is.
    
    **Steps to Reproduce**
    1. Go to '...'
    2. Click on '...'
    3. Observe error
    
    **Expected behavior**
    What should have happened?
    
    **Environment**
    - OS: [e.g. Ubuntu 22.04]
    - Version: [e.g. v1.2.3]
    4

    Branch & Workflow Strategies

    Gitflow on Gitea

    For teams managing multiple environments, a simplified Gitflow strategy works well with the CI/CD pipeline built in Parts 2 and 4:

    • main: Protected, requires PR + CI pass + 1 review. Auto-deploys to production.
    • develop: Protected, requires CI pass. Auto-deploys to staging.
    • feature/*: Short-lived branches for individual features. Open PRs targeting develop.
    • hotfix/*: Emergency fixes branched from main, merged to both main and develop.
    • release/*: Optional release preparation branch for changelog and versioning.

    Merge Strategies

    Configure allowed merge strategies per branch in Repository Settings → Merge Strategies. For clean history, restrict main to squash merges only:

    Merge strategy recommendation
    # Branch protection for main:
    # Allowed merge strategies: Squash only
    
    # This forces all PRs to become a single commit on main,
    # making git log readable and revert operations simple.
    
    # For develop, allow merge commits to preserve PR history:
    # Allowed merge strategies: Merge commit, Squash
    5

    Project Boards & Milestones

    Creating a Project Board

    Gitea project boards are kanban-style boards that can aggregate issues from across multiple repositories:

    • 1. Create a new Project under the organization
    • 2. Add columns: Backlog, In Progress, In Review, Done
    • 3. Link repositories: Project Settings → Linked Repositories
    • 4. Add issues to the board by assigning the project label in any linked repository

    Milestones & Releases

    Use milestones to group issues by release version. Automate release creation:

    .gitea/workflows/release.yml
    # Automate release creation via Gitea Actions
    on:
      push:
        tags: ['v*']
    
    jobs:
      release:
        runs-on: ubuntu-22.04
        steps:
          - uses: actions/checkout@v4
            with:
              fetch-depth: 0
    
          - name: Generate changelog
            run: |
              git log $(git describe --tags --abbrev=0 HEAD^)..HEAD \
                --pretty=format:'- %s (%an)' > CHANGELOG_LATEST.md
    
          - name: Create release
            uses: actions/create-release@v1
            env:
              GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
            with:
              tag_name: ${{ gitea.ref_name }}
              release_name: Release ${{ gitea.ref_name }}
              body_path: CHANGELOG_LATEST.md

    Production Security Hardening

    1. Disable Open Registration

    Unless you intend Gitea to be publicly accessible for self-signup, disable open registration immediately after setup:

    app.ini — Disable registration
    # In /opt/gitea/config/app.ini
    [service]
    DISABLE_REGISTRATION = true
    REQUIRE_SIGNIN_VIEW = true    ; require login to view any content
    
    # Restart Gitea to apply
    cd /opt/gitea && docker compose restart gitea

    2. Configure Rate Limiting & Security

    app.ini — Security settings
    [api]
    ENABLE_SWAGGER = false    ; disable Swagger UI in production
    MAX_RESPONSE_ITEMS = 50
    
    [security]
    LOGIN_REMEMBER_DAYS = 7
    MIN_PASSWORD_LENGTH = 16
    PASSWORD_COMPLEXITY = lower,upper,digit,spec
    
    [oauth2]
    ENABLE = true
    
    [server]
    # Limit access to admin-only API endpoints
    # by reverse proxy rules (see Nginx section below)

    3. Nginx Security Headers

    Nginx security headers
    # Add to your Nginx server block for Gitea:
    server {
        # ... existing proxy config ...
    
        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection '1; mode=block';
        add_header Referrer-Policy strict-origin-when-cross-origin;
        add_header Permissions-Policy 'camera=(), microphone=(), geolocation=()';
    
        # Block access to internal endpoints from the public internet
        location /api/internal/ {
            deny all;
        }
    }

    4. Fail2Ban for Brute Force Protection

    Install and configure Fail2Ban
    sudo apt install fail2ban -y
    
    # Create a Gitea jail
    sudo tee /etc/fail2ban/jail.d/gitea.conf << 'EOF'
    [gitea]
    enabled = true
    filter = gitea
    logpath = /opt/gitea/logs/gitea.log
    maxretry = 5
    findtime = 3600
    bantime = 86400
    action = iptables-allports
    EOF
    
    sudo tee /etc/fail2ban/filter.d/gitea.conf << 'EOF'
    [Definition]
    failregex = .*(Failed authentication attempt|invalid credentials|Attempted access).* for
    ignoreregex =
    EOF
    
    sudo systemctl restart fail2ban

    5. Two-Factor Authentication

    Enforce 2FA for all admin accounts and optionally all users. Gitea supports TOTP-based 2FA:

    • 1. Each user enables 2FA in User Settings → Security → Two-Factor Authentication
    • 2. For admins, enforce via Admin Panel → Users → select user → 'Prohibit Login Without 2FA'
    • 3. Gitea does not yet have a global 'require 2FA for all users' setting; use LDAP/OIDC integration for centrally managed 2FA enforcement

    6. Regular Security Updates

    /opt/gitea/update.sh
    #!/bin/bash
    # Run monthly or when security advisories are published
    
    cd /opt/gitea
    
    # Pull latest image
    docker compose pull gitea
    
    # Recreate container with new image
    docker compose up -d --force-recreate gitea
    
    # Verify Gitea is healthy
    sleep 10
    curl -s https://git.yourdomain.com/api/v1/version | jq '.version'

    Monitoring Your Gitea Instance

    Built-in Prometheus Metrics

    Gitea exposes Prometheus metrics at /metrics when enabled:

    app.ini — Enable metrics
    # app.ini
    [metrics]
    ENABLED = true
    TOKEN = a_secret_metrics_token    ; protect the endpoint
    
    # Prometheus scrape config (prometheus.yml):
    scrape_configs:
      - job_name: gitea
        bearer_token: a_secret_metrics_token
        static_configs:
          - targets: ['git.yourdomain.com']
        metrics_path: /metrics

    Log Management

    Log rotation
    # Tail live Gitea logs
    docker logs -f gitea --since 1h
    
    # Rotate and archive logs (via logrotate)
    sudo tee /etc/logrotate.d/gitea << 'EOF'
    /opt/gitea/logs/*.log {
        daily
        rotate 14
        compress
        missingok
        notifempty
    }
    EOF

    Gitea Administration Checklist

    Hardening TaskStatus
    Open registration disabled☐ Complete
    Admin account has 2FA enabled☐ Complete
    SSH key auth only (password auth disabled)☐ Complete
    Fail2Ban configured for SSH and web login☐ Complete
    Nginx security headers in place☐ Complete
    Automated nightly backups running☐ Complete
    Gitea auto-update script scheduled☐ Complete
    Prometheus metrics enabled and scraped☐ Complete
    Log rotation configured☐ Complete
    Branch protection on main + develop☐ Complete
    CODEOWNERS file in all critical repos☐ Complete
    CI status checks required before merge☐ Complete
    PATs rotated (schedule: every 90 days)☐ Complete
    Registry cleanup cron job running☐ Complete

    🎉 Series Complete: What You Have Built

    • Part 1: Gitea with PostgreSQL, organizations, SSH keys, and GitHub mirroring
    • Part 2: CI/CD with Gitea Actions or Woodpecker CI
    • Part 3: Package registry for Docker, npm, PyPI, and Helm
    • Part 4: Auto-deploy on push via Dokploy or Coolify
    • Part 5: Team workflows, CODEOWNERS, security hardening, and monitoring

    You're now running the equivalent of GitHub + GitHub Actions + Docker Hub + Vercel — all on a single VPS.