A Beginner-Friendly Guide to CI/CD with GitHub Actions
Build a pipeline you can explain: what CI and CD actually mean, how a GitHub Actions workflow is structured, and how to get from a test run to a safe deployment.
- #GitHub Actions
- #CI/CD
- #Docker
Open to interesting infrastructure problems
I build and run the boring parts of infrastructure well — pipelines that deploy predictably, databases that stay consistent under load, and systems whose failures are legible at 3am.
Toolkit
The stack I reach for when building delivery pipelines, running services and moving data between systems.
EC2, S3, RDS, IAM and VPC networking for application platforms.
Reproducible build and runtime images, multi-stage and slim bases.
Workload scheduling, probes, resource limits and rollout strategy.
Build, test and deploy pipelines with caching and reusable jobs.
Replication, query plans, connection pooling and safe migrations.
Change-data-capture pipelines into a columnar analytics store.
SLO-oriented metrics, alert routing and dashboards people read.
systemd, filesystems, networking and incident-time debugging.
Writing
Tutorials, architecture notes and things that broke in ways worth writing down.
Build a pipeline you can explain: what CI and CD actually mean, how a GitHub Actions workflow is structured, and how to get from a test run to a safe deployment.
Adding read replicas is easy; routing to them safely is not. How to split reads and writes without letting replication lag turn into user-visible bugs.
A calm, ordered procedure for a server at 100% disk usage — find the space, understand what is holding it, and reclaim it without deleting something the system still needs.
Selected work
Longer-form write-ups of systems I have designed or operated. Figures shown are illustrative examples, clearly labelled as such.
Streaming row changes out of a transactional MySQL cluster into a columnar store so analytics stops competing with production traffic.
Reporting queries ran against the same MySQL primary that served the application. As the dataset grew, a single analyst refreshing a dashboard could add noticeable latency to ordinary writes. The goal was to move analytical reads off the transactional database without introducing a nightly batch window.
The pipeline reads the MySQL binary log with Debezium, publishes change events to Kafka, and materialises them in ClickHouse. Deletes and updates are handled with a ReplacingMergeTree keyed on the primary key plus a monotonic version column, so the analytics tables converge on the same state as the source without requiring in-place mutations.
The interesting part was not the happy path but the operational envelope: schema changes, snapshot restarts, replication lag alerting, and deciding what "eventually consistent" is allowed to mean for each dashboard.
Illustrative figures — example environment, not measured production results
A CI/CD setup where the artifact that passes tests is the exact artifact that reaches production.
Teams frequently rebuild an image per environment, which quietly breaks the guarantee that what was tested is what ships. This pipeline builds a single image, tags it with the commit SHA, and promotes that same digest through staging and production.
Pull requests run lint, unit tests and a container build. Merges to the default branch push the image to a registry, run integration tests against it, and then deploy. Deployment jobs use GitHub environments so production requires an explicit approval and holds its own secrets.
Caching is deliberately conservative: dependency caches are keyed on lockfile hashes, and layer caching is scoped per branch so a poisoned cache cannot silently leak into main.
Illustrative figures — example environment, not measured production results
About
I work on the parts of a system that only get noticed when they fail: build and deploy pipelines, database replication, log and metric plumbing, and the runbooks that make an incident boring.
Most of what I publish here comes out of that work — an architecture I had to reason through, a failure mode that surprised me, or an explanation I wished had existed when I was learning.