Deployment.md
$ cat deployment.md

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:

  1. GET the current ACL from the Linode API, save it verbatim.
  2. PUT a modified ACL that adds the runner’s egress range.
  3. Run the deploy.
  4. 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 /readyz then /api until 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.

Index