For almost all my CSE learning time, I thought CI/CD was a simple pipeline/flow that just checks and deploys stuff. Don't get me wrong, I only got a taste of CI/CD through services like Vercel and Render and never thought to look deeper into it. But when I decided to implement a CI/CD pipeline myself for my project, my view completely changed!!
GitHub Actions
So I started with the basic/famous one, GitHub Actions, and the amount of things I learnt was unreal.
I learnt that CI (Continuous Integration) is basically about automatically checking whether the newest codebase still works and builds correctly, while CD (Continuous Deployment) is where that code actually gets deployed to the server.
In my case, that meant: pull → Docker build → Docker up, basically deploying the newest version of the codebase.
Just like Docker Compose files, GitHub Actions provides many configurations that were super helpful for me.
Stuff like events (when to run the pipeline, like on a code push or when the workflow is manually triggered), jobs (we can have multiple builds, like frontend and backend, and build/test each one independently), and mainly runners (these were game changers for me).
Runners
GitHub Actions needs some machine to build and test this code, and GitHub provides us with that too!! GitHub provides hosted Ubuntu,Windows and MacOS machines to run the CI/CD flow, Also, these machines are ephemeral. Meaning, after a job is completed, the machine is automatically cleared, ready for GitHub to provide a fresh environment for another job.
CI (Continous Integration)
So I set up my CI and ran it. As I said, we can have multiple jobs running, and we can also make jobs depend on one another. So, if I want to build the frontend and backend together and make the Docker build wait for both of them, it will not run until the frontend and backend jobs finish successfully. If either of them fails, the docker build doesn't run.
And honestly, making jobs wait for other jobs is a good pipeline decision. Instead of having one massive job where everything runs together and you have to dig through a whole-ass console to find what went wrong, you can separate each stage into its own job and see exactly which part failed. This makes debugging much easier, especially as the pipeline gets bigger and more complex.
CD (Continous Deployment)
Now comes the fun part. GitHub Actions now needs to SSH into my server, deploy the CI-tested, production-ready code, build the Docker image, and finish the deployment... right??
NOPE.
As I mentioned in my $0 Cloud Series (feel free to check it out), my server is behind CGNAT, which means I don't have a publicly reachable IP for incoming connections. So GitHub Actions can't simply SSH directly into my server.
I initially considered two options: get a public IP (not really an option with my current setup) or use the same Cloudflare tunnel I was already using to connect my server to the outside world.
But there was a third option: using my own server as the runner!
We can use the machines GitHub provides as runners, but we can also use our own server as a runner. So, in this setup, GitHub Actions acts as the orchestrator — it manages and sends the jobs, while my own server actually executes the CD jobs using its own resources. And it was perfect for my CGNATed server.
Note: Self-hosted runners execute workflow jobs on your own infrastructure. That means you need to be careful about what workflows can run on that runner, especially if you're allowing untrusted pull requests or workflows from people outside your repository. For that reason, self-hosted runners are generally better suited to private repositories and trusted workflows.
So, my CI runs on GitHub-hosted machines, while my CD runs on my own server.
I followed a couple of steps provided by GitHub, set up my own server as a runner, and ran the whole CI/CD pipeline. And it worked!!!
What started as ‘CI/CD is just a pipeline that checks and deploys code’ turned into learning about workflows, jobs, runners, dependencies, self-hosted infrastructure, and even how CGNAT affects deployment architecture.
Top comments (0)