Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
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.
Installing Jenkins: LTS with Docker, Java 21, the First Admin and Choosing Plugins
You install Jenkins in Docker with a version-pinned LTS image, a named JENKINS_HOME volume and Java 21, and you skip the setup wizard with plugins.txt and JCasC. The anonymous volume trap, initialAdminPassword, the Docker-in-Docker TLS certificate error, anonymous access, zero executors on the controller, the CSRF crumb, agent-to-controller security and installing from the Linux package.
Your First Pipeline with a Jenkinsfile: Declarative Syntax, stages, steps, post and environment
A Jenkinsfile describes the pipeline in the same repository as the code; in Declarative syntax agent, stages and steps form the skeleton, while environment, options, parameters, triggers and post define the behavior. Linter output, the quoting rule, the parameter the shell sees as empty on the first build, a stage skipped by when, Snippet Generator and Replay, Scripted Pipeline, and NotSerializableException with @NonCPS.
Jenkins Agents: SSH Agents, Inbound Agents, Labels and Building Inside Docker Containers
With an SSH agent the controller opens the connection, with an inbound agent the agent does; you run the build on an agent chosen by label expressions or inside an SDK container with agent { docker }. Agent definitions with JCasC, real SSH and WebSocket connection logs, Docker Pipeline's workspace mount and the uid 1000 detail, disk usage with cleanWs, and a quick look at Kubernetes pod templates and Windows agents.
Jenkins Credentials: withCredentials, Credentials Binding and the Groovy String Interpolation Trap
You expose a secret in the narrowest scope with credentials() or withCredentials and read it in sh as a shell variable inside single quotes; Groovy interpolation with double quotes leaks it to the process. Credential types and SYSTEM/GLOBAL/folder scopes, readFile in JCasC, masking that catches base64 but misses a fragment, sshagent, team isolation with folder credentials and external secret stores.
Jenkins Multibranch Pipelines and GitHub Integration: Webhooks, PR Builds and Status Checks
A multibranch pipeline opens a job automatically for every branch and PR; a webhook starts the build within seconds and the result returns to the commit as a status check. Branch source traits, a webhook blocked by a localhost URL and Gitea's SSRF protection, telling branches and PRs apart with when, a fork PR's Jenkinsfile read from the base branch, required checks, and a GitHub App instead of a PAT.
A .NET Pipeline with Jenkins: dotnet SDK Container, Test Results, Coverage and Publish Artifacts
We restore, build, test and publish Bookstore.Api in the .NET SDK container and record the JUnit report, Cobertura coverage and a fingerprinted artifact in Jenkins. The NuGet cache volume's permission error and the time it saves, xUnit v3's JUnit output with Microsoft.Testing.Platform in .NET 10, the coverage quality gate, and a build that turns red when no test report is found.
An Angular Pipeline with Jenkins: Node Container, npm Cache, Headless Tests and stash/unstash
We build the Angular 22 app in a node:24 container with npm ci, ng lint, Vitest and ng build in parallel with the .NET API, and combine both into one package with stash/unstash. The npm cache volume, Vitest's JUnit reporter, budgets, the missing-package error for browser mode, not stashing a 329 MB node_modules, and parallel stage durations in Pipeline Graph View.
Parallel, Matrix, input and Deploy Stages in Jenkins
We deploy the same package first to staging and, after an input approval that holds no agent, to production with sshagent, rsync and systemctl; Lockable Resources solves concurrency and milestone solves ordering. A deploy script with a health check, real lock output where an old build deploys after a new one, inversePrecedence, failFast, a Node 22/24 matrix and the app_offline.htm structure for IIS.
Jenkins Shared Libraries: Sharing Pipeline Code with vars/ and src/
A Shared Library keeps repeated pipeline code in a separate repository as vars/ steps and src/ classes; pipelines load it pinned to a tag with @Library('lib@v1.2.0'). The dotnetCi and angularCi steps, a global library via JCasC, the risk of an unpinned main, real output of the difference between a trusted global library and a sandboxed folder library, a JenkinsPipelineUnit test, and when not to write a library.
Managing Jenkins as Code: Configuration as Code (JCasC), plugins.txt and Backups
You manage Jenkins as code with a Dockerfile, a pinned plugins.txt and JCasC YAML in Git, and back up only the state in JENKINS_HOME. The YAML's structure, the export's encrypted-secret trap, a manual change undone on reload, --available-updates, no bundled plugins in the war since 2.580.1, a 1.7 MB backup, a real restore test and the LTS upgrade flow.
Reading and Migrating Old Jenkins Setups: Freestyle to Pipeline, Blue Ocean to Pipeline Graph View, Java 17 to 21
You read an old Jenkins with an inventory first, then migrate step by step: Java 21 first, then the Jenkins version, then jobs. The real message 2.580.2 prints when refusing to start on Java 17, the Java timeline for controllers and agents, the Jenkinsfile equivalent of a Freestyle config.xml, Pipeline Graph View instead of the deprecated Blue Ocean, JNLP to WebSocket, and a quick look at GitHub Actions Importer.