Files
homelab-design/decisions/0020-internal-registry-for-cicd.md
Billy D. 3a46a98be3
All checks were successful
Update README with ADR Index / update-readme (push) Successful in 6s
docs: add ADR index workflow, standardize all ADR formats
- Add Gitea Action to auto-update README badges and ADR table on push
- Standardize 8 ADRs from heading-style to inline metadata format
- Add shields.io badges for ADR counts (total/accepted/proposed)
- Replace static directory listing with linked ADR table in README
- Accept ADR-0030 (MFA/YubiKey strategy)
2026-02-09 17:25:27 -05:00

4.8 KiB

Internal Registry URLs for CI/CD

  • Status: accepted
  • Date: 2026-02-02
  • Deciders: Billy
  • Technical Story: Bypass Cloudflare 100MB limit by using internal registry endpoints for CI pushes

Context

Factor Details
Problem Cloudflare proxying limits uploads to 100MB per request
Impact Docker images (20GB+) and large packages fail to push
Current Setup Gitea at git.daviestechlabs.io behind Cloudflare
Internal Access registry.lab.daviestechlabs.io bypasses Cloudflare

Our Gitea instance is accessible via two URLs:

  • External: git.daviestechlabs.io - proxied through Cloudflare (DDoS protection, caching)
  • Internal: registry.lab.daviestechlabs.io - direct access from cluster network

Cloudflare's free tier enforces a 100MB upload limit per request. This blocks:

  • Docker image pushes (multi-GB layers)
  • Large Python package uploads
  • Any artifact exceeding 100MB

Decision

Use internal registry URLs for all CI/CD artifact uploads.

CI/CD workflows running on Gitea Actions runners (which are inside the cluster) should use registry.lab.daviestechlabs.io for:

  • Docker image pushes
  • PyPI package uploads
  • Any large artifact uploads

External URLs remain for:

  • Git operations (clone, push, pull)
  • Package downloads (pip install, docker pull)
  • Human access via browser

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         INTERNET                                 │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │   Cloudflare    │
                    │  (100MB limit)  │
                    └────────┬────────┘
                             │
                             ▼
              ┌──────────────────────────────┐
              │  git.daviestechlabs.io       │
              │  (external access)           │
              └──────────────────────────────┘
                             │
                             │  same Gitea instance
                             │
              ┌──────────────────────────────┐
              │  registry.lab.daviestechlabs │
              │  (internal, no limits)       │
              └──────────────────────────────┘
                             ▲
                             │ direct upload
                             │
              ┌──────────────────────────────┐
              │     Gitea Actions Runner     │
              │     (in-cluster)             │
              └──────────────────────────────┘

Consequences

Positive

  • No upload size limits for CI/CD artifacts
  • Faster uploads (no Cloudflare proxy overhead)
  • Lower latency for in-cluster operations
  • Cost savings (reduced Cloudflare bandwidth)

Negative

  • Two URLs to maintain in workflow configurations
  • Runners must be in-cluster (cannot use external runners for uploads)
  • DNS split-horizon required if accessing from outside

Neutral

  • External users can still pull packages/images via Cloudflare URL
  • Git operations continue through external URL (small payloads)

Implementation

Docker Registry Login

- name: Login to Internal Registry
  uses: docker/login-action@v3
  with:
    registry: registry.lab.daviestechlabs.io
    username: ${{ secrets.REGISTRY_USER }}
    password: ${{ secrets.REGISTRY_TOKEN }}

PyPI Upload

- name: Publish to Gitea PyPI
  run: |
    twine upload \
      --repository-url https://registry.lab.daviestechlabs.io/api/packages/daviestechlabs/pypi \
      dist/*

Environment Variable Pattern

For consistency across workflows:

env:
  REGISTRY_EXTERNAL: git.daviestechlabs.io
  REGISTRY_INTERNAL: registry.lab.daviestechlabs.io