What happens on git push origin main
The whole pipeline is in .github/workflows/main.yaml. Each step:
1. Open the cluster API ACL
The cluster’s Control Plane ACL restricts who can reach the
Kubernetes API. The runner needs to send kubectl apply, so the
workflow temporarily extends the ACL for the duration of the job:
- GET the current ACL from the Linode API, save it verbatim.
- PUT a modified ACL that adds the runner’s egress range.
- Run the deploy.
- PUT the original ACL back. This step runs even on failure
(
if: always()) so the cluster is never left exposed if a build step blows up.
2. Verify the cluster exists
A simple GET against the Linode API by cluster ID. The workflow never creates or modifies clusters — node pools, k8s version, HA settings are all managed manually.
3. Build the Hugo site
- uses: peaceiris/actions-hugo@v2
- run: hugo
Output lands in ./public.
4. Build and push the container image
docker build --no-cache -t <registry-user>/woodard-tech:latest .
docker push <registry-user>/woodard-tech:latest
--no-cache ensures every commit produces a fresh image even if
nothing else changed.
5. Apply the manifest
kubectl apply --validate=false -f k8s.yaml
--validate=false skips client-side schema validation. That
validation downloads /openapi/v2 from the API server, which LKE’s
control plane sometimes EOFs mid-stream. Server-side validation
still runs on apply.
The step also includes:
- A warmup phase that polls
/readyzthen/apiuntil the apiserver consistently responds. This catches ACL propagation delays — a fresh entry takes a few seconds to reach all control plane replicas. - A retry loop around the apply itself. Discovery requests occasionally fail and only some resources get applied. Re-running the apply is safe — it’s idempotent.
6. Roll out and verify
kubectl rollout restart deployment/woodard-tech
kubectl rollout status deployment/woodard-tech --timeout=5m
kubectl get service woodard-tech -o wide
Each call is wrapped in the same retry helper so a single flaky discovery doesn’t tank the run.
7. Restore the ACL
Always runs. PUTs the saved-original ACL back to the cluster, no matter whether the deploy succeeded or failed.
Secrets the workflow needs
The workflow authenticates to a few services at deploy time, all via GitHub’s encrypted secrets — never committed to the repo:
- a Linode API token, to adjust the Control Plane ACL
- a restricted cluster kubeconfig, to apply the manifest
- container registry credentials, to push the image
Why deploys can take a couple of minutes
Most of the time is spent waiting for the LKE control plane to respond consistently from a freshly allowlisted IP. The Hugo build itself takes under 30 ms; the container build is a few seconds. Once the cluster is responsive, the apply and rollout are quick.