May 2026 · Part 1 of a 5-part series on migrating CI/CD from GitHub to Codeberg
The Kikitoru platform (a Japanese-language learning application running on Google Kubernetes Engine) executes its core CI/CD pipeline between 10 and 20 times per working day. Under optimal operational conditions, the managed service layer provided by GitHub Actions performs within baseline parameters. However, infrastructure failures within their network introduce absolute blocking points that cannot be remediated externally due to a lack of host-level infrastructure control.
Over a multi-month observation window, a distinct failure pattern emerged: deployment pipelines failed while GitHub’s external status monitoring indicators reported normal operating conditions. This discrepancy repeatedly forced engineering teams to spend cycles debugging internal application configurations and security credentials before isolating the root cause as an upstream provider degradation.
GitHub reports service availability based on synthetic internal health checks executing within its own network boundary. This methodology yields an official availability metric between 99.5% and 99.9%.
From an external architectural perspective—where an independent pipeline must compile assets and push containers across distinct cloud provider boundaries—the actual degradation rate was significantly higher.
waiting for a runner). The execution delays fluctuated non-deterministically between 30 seconds and 10 minutes. Because the service eventually self-corrected, these events never triggered provider incident reports.docker push operations targeting ghcr.io intermittently returned HTTP 503 Service Unavailable status codes. While subsequent retries occasionally resolved the connection, unacknowledged registry outages frequently persisted for hours, blocking artifact promotion.Infrastructure failures occur across all cloud systems; however, the primary operational constraint is the transparency gap. When a third-party status dashboard displays All Systems Operational during an active platform disruption, it introduces a dual dependency failure:
This operational blind spot eliminates the ability to rapidly determine if a pipeline failure is an internal configuration error or an upstream service fault. Engineering diagnostics degenerate into speculative troubleshooting, wasting resource hours validating local configurations, access tokens, and image layers before identifying an unannounced registry outage.
Transitioning to a private Forgejo runner architecture natively deployed inside our existing GKE environment binds CI availability metrics directly to the target cloud provider’s structured Service Level Agreements (SLAs):
More significantly, this layout shifts the diagnostic boundary into our own observability stack. The runner operates as a native pod within a managed Kubernetes namespace. Execution output routes directly to internal log aggregators, and runtime performance metrics stream to localized Grafana instances.
System behaviors—such as cold-start latency during spot instance provisioning—are visible on internal dashboards, eliminating hidden infrastructure anomalies.
Transitioning away from a managed service model requires the engineering team to assume full lifecycle maintenance of the CI/CD plane. This includes executing runner engine updates, managing Kubernetes cluster manifests, and resolving Docker-in-Docker (DinD) storage and security layers. These tasks represent well-defined, predictable engineering workflows rather than untraceable infrastructure failures.
The decision to migrate was driven entirely by system reliability and telemetry visibility requirements. The architecture shift would have been executed even if self-hosting incurred higher monthly compute costs. The 10x reduction in operational expenditures remains a secondary structural benefit rather than the primary driver of the project.