Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Environments with Kustomize: Bases, Overlays, Image Tags and configMapGenerator
With Kustomize shared manifests live in a base, each environment writes only its differences in an overlay, and configMapGenerator's hash suffix triggers a rollout when config changes. With real output we share an HPA/PDB through a component, compare JSON Patch with strategic merge patch, run kustomize edit set image in CI, and watch commonLabels change a selector and take the service down through a half-finished apply.
What Is Jenkins? Controllers, Agents, Executors and When to Choose Jenkins Over GitHub Actions
Jenkins is a self-hosted CI/CD server made of a controller that plans the work and agents that run builds on their executors. Its history since Hudson, workspaces and labels, how an unmatched label leaves a build waiting in the queue, the price of the plugin ecosystem, Freestyle vs Pipeline, the LTS and weekly lines, and when Jenkins is the right choice over GitHub Actions.
Reading Old GitHub Actions Workflows: From set-output to GITHUB_OUTPUT, From Node 12/16/20 to Node 24
When updating an old workflow, change the runner label, action majors, SDK source, workflow commands and cache key together: ubuntu-18.04, @v2 actions and ::set-output either no longer work or warn in 2026. A 2018–2026 timeline, the Node 12→24 transitions, actionlint and act output for a 2021-style .NET 5 workflow, a checklist for moving to .NET 10 and a summary of 2026.
Debugging, Speed and Cost in GitHub Actions: Debug Logging, act, Cache Strategy and Artifact Retention
For debugging, turn on ACTIONS_STEP_DEBUG/ACTIONS_RUNNER_DEBUG for a single re-run, shorten the loop with act, build cache keys from the lock file hash with restore-keys, and keep artifacts briefly. Real cache miss, hit and restore-keys output from three act runs, a list of act's limits, the 10 GB quota and 7-day rule, retention, billing screens and a speed checklist.
GitHub Actions Runners: Hosted Images, the ubuntu-latest Migration, ARM, Larger Runners and Self-Hosted
Pin the runner label in release and deploy pipelines: ubuntu-latest moves from 24.04 to 26.04 between October 19 and November 19, 2026. The hosted image map, arm64 and larger runners, actionlint's label check, why a label in act is only a Docker image mapping, ephemeral self-hosted runners, the minimum version rule, ARC, 2026 prices and how multi-cloud affects IP allowlists.
GitHub Actions Security: SHA Pinning, Lockfiles, pull_request_target and Preventing Script Injection
Pin actions to full commit SHAs and keep them current with Dependabot and lockfiles, pass untrusted values through env: instead of run:, and never run PR code in pull_request_target. The tj-actions case, zizmor and actionlint findings, gh actions-lock, real script injection output under act, checkout@v7 and the December 2, 2026 change, a safe workflow_run pattern and read-only cache tokens.
Reducing Duplication in GitHub Actions: Composite Actions, Reusable Workflows and YAML Anchors
Use a composite action to share steps, a reusable workflow to share jobs that need environments and approvals, and YAML anchors to repeat a block within one file. Bookstore's dotnet-ci action and reusable-deploy workflow under act; the risks of secrets: inherit, environment secrets, the 10-level/50-workflow limits, the $/ syntax, versioning and why merge keys are not supported.
GitHub Actions Environments: Protection Rules, Approvals and Deployment History
An environment is a named target that applies protection rules (required reviewers, wait timers, branch/tag policies) before a job reaches a runner, exposes its secrets only to that job and keeps a deployment history. Bookstore's staging/production deploy workflow, plan limits for private repositories, deployment: false, per-environment concurrency and queue: max; where act and actionlint stop, with real output.
Measuring and Speeding Up a Pipeline: DORA Metrics, Caching and Parallelism
DORA's current five metrics (change lead time, deployment frequency, failed deployment recovery time, change fail rate, deployment rework rate) are calculated from deploy records and should be tracked as team-level trends; speeding up a pipeline starts with measuring. A single-file C# calculator, a real act measurement where a NuGet cache saved only 0.2 seconds of restore at a 2.3-second restore cost, cache key design, and the path filter trap.
Pipeline Security and Secrets: Least Privilege, Short-Lived Credentials and Supply Chain
The pipeline is the most powerful user, holding production keys; keeping secrets in a store, least privilege per job, short-lived tokens via OIDC instead of long-lived cloud keys and pinning actions to commit SHAs close most of the risk. Real act output showing a base64-encoded secret escaping masking, script injection caught by actionlint, fork PR risks, and a short look at SLSA v1.2 and SBOMs.
Approvals, Gates and Deployment Controls: Adding People and Rules to a Pipeline
Automated gates (smoke tests, health checks, deploy freezes) should answer 'does this change work?', while a manual approval should answer 'now?'; DORA found no evidence that heavyweight external approvals lower the change fail rate. Real output showing that a Jenkins input step's submitter does nothing when everyone is an administrator, GitHub environment protection rules and Azure DevOps checks.
Environments and Promotion: Dev, Test, Staging, Production and Configuration
The same artifact moves through dev, test, staging and production without being rebuilt; the only thing that should change from step to step is configuration coming from the environment. Layered configuration in ASP.NET Core with appsettings.{Environment}.json and environment variables, a missing setting that fails at startup with ValidateOnStart, a difference inventory between staging and production, preview environments per PR, and masking non-prod data.