Practice: CI/CD for models and services with GitHub Actions
Generated by Claude through GigAI 0.1.11.10 on 2026-10-11.
Main tool: GitHub Actions · GitHub Actions keeps the workflow definition next to the model code, reacts to repository events, and gives you reusable workflows, protected environments and self-hosted runners, which are the building blocks this lesson asks a platform engineer to design. · Alternatives: GitLab CI keeps pipelines in one file with built-in environments and its own runners, Jenkins is self-managed and plugin-driven with more operational upkeep, Argo Workflows and Tekton run pipelines as Kubernetes resources and suit heavy training jobs on a cluster, and CircleCI is a hosted engine with a similar job model but a separate account outside the code host.
Every step links into the tool's own documentation; links were checked on 2026-10-11 · version 1
Setup (one time)
1. Create a free GitHub account and a public practice repository holding a small scikit-learn training script, an evaluation script that writes a metrics file, and a few pytest tests.
Every later step needs a repository with something real to test, train and release. A public repository keeps hosted runner minutes free, and a CPU-only model keeps everything runnable without a GPU.
Explained in: Quickstart for GitHub Actions - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The repository exists, you can push to it, and its Actions tab is visible.
2. Read the overview of workflows, events, jobs, actions and runners before writing any YAML.
These five terms are the vocabulary for the whole lesson, and each later step changes exactly one of them.
Explained in: Understanding GitHub Actions - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: You can explain in one sentence each how an event, a job, a step, an action and a runner relate.
3. Install a container runtime, a local Kubernetes cluster tool such as kind or minikube, kubectl and Helm on your laptop.
The last steps run Actions Runner Controller on a local cluster as the free substitute for a managed cloud cluster. Installing these now keeps the production-like stage from stalling.
Explained in: Actions Runner Controller - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: A local cluster starts, kubectl lists its nodes, and Helm reports its version.
Steps
1. Create your first workflow file in the workflows directory of the repository, commit it, and open the run it triggers.
A first run shows the full loop of event, job, runner and log before any ML logic is added.
Explained in: Quickstart for GitHub Actions - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The Actions tab shows a completed run with a green check and you can expand each step log.
2. Write a CI workflow with separate lint and test jobs, where each job declares its runner and lists steps that check out the code, install dependencies and run pytest.
This is workflow anatomy in practice: triggers at the top, jobs under them, steps inside jobs. Splitting lint and test shows that jobs run in parallel unless one declares a dependency on another.
Explained in: Workflows - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: A pull request shows two separate job checks, and a deliberately broken test turns the test job red.
3. Configure the CI workflow to trigger on pull requests and pushes to the main branch, with path filters so that documentation-only changes do not start it.
ML repositories mix code, configuration and notebooks, so a platform engineer uses filters to keep expensive jobs from running on irrelevant changes.
Explained in: Workflow syntax for GitHub Actions - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: A commit that edits only a README starts no run, and a commit that edits the training script does.
4. Create a training workflow that trains the model, then uploads the model file and the metrics file as a workflow artifact, and compare the metrics from two runs made with different hyperparameters.
Models and metrics are the extra artifacts that separate ML delivery from ordinary CI. Persisting them per run is what makes a later promotion decision auditable.
Explained in: Workflow artifacts - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: Two runs each list a downloadable artifact, and you can state which run has the better metric.
5. Enable a schedule trigger on the training workflow so it retrains on a cron cadence as well as on demand.
Continuous training is the third loop next to CI and CD: the model is rebuilt because time or data moved on, not because code changed. A schedule is the simplest form of that loop.
Explained in: Workflow syntax for GitHub Actions - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The Actions tab shows a training run whose trigger is the schedule rather than a push.
6. Package the test, train and evaluate logic as a reusable workflow that declares typed inputs such as the model name and a metric threshold, and exposes the model version as an output.
Reusable workflows are the shared building blocks a platform team hands to every model team. Typed inputs and outputs form the contract between the shared workflow and its callers.
Explained in: Workflow syntax for GitHub Actions - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The reusable workflow file is accepted without syntax errors and its inputs and outputs appear when another workflow calls it.
7. Write a release workflow that calls the reusable workflow, then chains jobs for evaluation gate, container image build with Docker Buildx, staging deploy and production deploy, each depending on the one before.
This gives the model release pipeline its shape: one ordered graph where a failed evaluation stops the image build and the deploy. If pushing the image would need a paid cloud registry, push to the free package registry of the code host instead.
Explained in: Reusing workflow configurations - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The run graph shows the jobs in order, and lowering the model quality below the threshold stops the pipeline before the build job.
8. Create staging and production environments in the repository settings, add yourself as a required reviewer on production, and point the two deploy jobs at them.
Protected environments put a human approval between a trained model and production. That approval is the control auditors and model owners expect.
Explained in: Deployments and environments - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The production deploy job pauses in a waiting state until you approve it in the run page.
9. Configure an environment secret with a different value in staging and in production, and use it in the deploy jobs in place of any repository-wide credential.
Scoping credentials to an environment means a job can read the production secret only after the protection rules pass. This limits what a compromised pull request can reach.
Explained in: Deployments and environments - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The staging job cannot read the production secret, and the logs show the secret value masked.
10. Configure every job to name a specific runner image version instead of the latest label, and pin the Python version and the dependency lock file used in training.
A retraining run must be reproducible months later. A floating runner image changes preinstalled software underneath you without warning.
Explained in: GitHub-hosted runners - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: Two runs a day apart report the same image version and package versions in their setup logs.
11. Deploy Actions Runner Controller and one runner scale set onto your local cluster with Helm, authenticating it to your practice repository.
Platform teams run training and build jobs on their own Kubernetes runners for network access, cost and custom hardware. A local cluster stands in for the managed cloud cluster used in production.
Explained in: Deploying runner scale sets with Actions Runner Controller - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: The controller and listener pods are running, and the scale set appears as a runner in the repository settings.
12. Configure minimum and maximum runner counts on the scale set, switch the release workflow to run on the scale set name, and run the full pipeline end to end.
Autoscaling limits are how a platform keeps idle cost at zero while capping the load from many model teams. Running the whole release on your own runners is the closest laptop equivalent of a production setup.
Explained in: Deploying runner scale sets with Actions Runner Controller - GitHub Docs (docs.github.com), checked 2026-10-11
Done when: Runner pods appear when the pipeline starts and disappear after it ends, and the release finishes after your production approval.