Conquering CI/CD: My GitOps Pipeline is Live!
Plamen Florov
Hey everyone,
The full CI/CD pipeline for my DevOps blog is now fully operational! After a solid journey of learning and quite a bit of troubleshooting, I can confirm that a git push on my blog repo now automatically results in a live update on my K3s homelab cluster. This feels like a huge step forward in my automation goals.
The Full Cycle
It’s truly satisfying to see the entire process work seamlessly:
- Content Creation: I write a blog post in Markdown and commit it to my
devops-blogrepository. - GitHub Actions (CI):
- My workflow automatically triggers.
- It pulls the latest code, including new posts.
- It builds a fresh Docker image of my Hugo site.
- This image is then pushed to GitHub Container Registry, tagged with
latestand a unique Git SHA.
- GitHub Actions (CD Trigger):
- The same GitHub Actions workflow then checks out my separate
k3s-homelabrepository. - It uses
yqto update theblog-deployment.yamlfile ink3s-homelabto point to the newly built Docker image tag. - It commits this manifest change and pushes it back to the
k3s-homelabrepo.
- The same GitHub Actions workflow then checks out my separate
- Flux CD (GitOps Controller on K3s):
- Flux, running directly on my K3s cluster, is constantly monitoring the
k3s-homelabrepository. - It detects the new commit with the updated
blog-deployment.yaml. - It pulls the changes and applies them to the cluster.
- Kubernetes (orchestrated by Flux) then pulls the new Docker image, updates the running blog pods, and brings the new content live.
- Flux, running directly on my K3s cluster, is constantly monitoring the
Key Learnings Along the Way
This wasn’t a straight path. I encountered and overcame several important challenges:
- Docker Buildx Caching: Figuring out how to properly configure
docker/setup-buildx-actionto leverage GitHub Actions cache. - GHCR Naming Conventions: Learning that repository names must be lowercase for Docker image tags in GitHub Container Registry.
- GHCR Permissions: Granting the
GITHUB_TOKENexplicitpackages: writepermission to push images to GHCR from an organization-owned repo. - Flux CD Bootstrap &
kubeconfig: Correctly installing Flux on K3s and ensuring my localfluxCLI used the rightkubeconfigto connect via Tailscale. - Kubernetes Manifest Namespaces: Ensuring all my manifests explicitly declared their
metadata.namespacefor Flux’sKustomizationto apply them correctly. yqSyntax: A surprisingly tricky one! Getting the exactyq -i 'expression' file.yamlsyntax right for in-place YAML updates within GitHub Actions.
My homelab now has a robust, secure, and automated deployment process, which is exactly what I aimed for!