{"id":740,"date":"2026-10-19T02:00:00","date_gmt":"2026-10-19T02:00:00","guid":{"rendered":"https:\/\/heartcareforyou.in\/blog\/?p=740"},"modified":"2026-10-19T02:00:00","modified_gmt":"2026-10-19T02:00:00","slug":"gitops-step-by-step-adoption-runbook","status":"publish","type":"post","link":"https:\/\/heartcareforyou.in\/blog\/gitops-step-by-step-adoption-runbook\/","title":{"rendered":"Adopting GitOps Step by Step: A Working Runbook"},"content":{"rendered":"<p>Most teams do not fail at GitOps because the tools are hard. They fail because they skip the boring decisions and start clicking. This runbook puts the boring decisions first, in the order that saves the most pain later, and keeps each step short enough to finish in one sitting. The premise, in one line: you describe how your systems should look, agents pull that description and reconcile the live platform against it, and nothing reaches production without a reviewed change. Everything below serves that line. Readers of this site will recognise the instinct \u2014 the whole point of a shared classification is that two people looking at the same patient reach for the same words, and the whole point of GitOps is that two engineers looking at the same system read the same definition of what it should be.<\/p>\n<h2>Step 1: State the GitOps model in one sentence<\/h2>\n<p>Before tooling, agree the sentence above and its four conditions: the destination is described, not the route; the description sits in a store that keeps every receipt and allows no silent rewrites; the platform&#8217;s own agents come and collect it; and the checking between declaration and reality never stops \u2014 a poll every couple of minutes fully qualifies. If a proposed tool or workflow breaks any of those four conditions, it is not the model, whatever the slide deck claims. A pipeline that finishes by applying changes straight to the cluster meets, at best, half the definition. Holding this line early prevents most later arguments, because every future debate reduces to a sentence everyone already signed.<\/p>\n<h2>Step 2: Make the day-zero decisions on paper<\/h2>\n<p>Five choices, made once and written down as a short decision record:<\/p>\n<ul>\n<li><strong>The store.<\/strong> Git is usual but not mandatory; a registry or versioned bucket holding immutable revisions also satisfies the model.<\/li>\n<li><strong>The topology.<\/strong> Whether a single central service steers every cluster, each cluster carries its own agent, or a central service feeds pulling agents out in the field \u2014 decide by how many clusters you honestly run and who operates them at night.<\/li>\n<li><strong>The layout.<\/strong> Where apps, infrastructure, and tenant configurations live, so the next engineer does not have to guess.<\/li>\n<li><strong>Secrets handling.<\/strong> Encrypted files in the store, sealed secrets, or a synchronisation operator \u2014 anything except plaintext credentials in a repository.<\/li>\n<li><strong>The drift policy.<\/strong> For each class of resource, decide in advance whether the agent reverts, tolerates, alerts, or protects. More on this in step five.<\/li>\n<\/ul>\n<p>Alongside those choices, adopt five house rules and treat them as invariants: one sanctioned route to production; nothing referenced by a pointer that can move on its own; reconcilers running with the least privilege that works; deletion guarded, always; and data kept out of the store entirely. Teams that skip this page of paperwork tend to rediscover each rule as an outage.<\/p>\n<h2>Step 3: Lay out the repository like a well-run ward<\/h2>\n<p>Structure the store with four top-level directories \u2014 clusters, infrastructure, tenants, apps \u2014 so that a newcomer can find the definition of anything in two moves. Then hold two rules that matter more than they look. First, an environment is a directory, never a branch: branch-per-environment setups decay into long-lived merges where staging and production silently diverge, which is precisely the disease this model exists to cure. Second, promotion moves an exact artefact, not a rebuild \u2014 you advance the pinned digest of an image that already passed staging, so what you tested is what you ship. Keep each overlay small enough to change one value at a time, and let the pipeline render the manifests while humans review the rendered output. A rendered diff shows the truth of a change; a values file rarely does.<\/p>\n<h2>Step 4: Bootstrap on day one, with the safety catch on<\/h2>\n<p>Install the reconciler and point it at the store \u2014 and make its first assignment itself, so the tool that keeps production aligned is managed through the same review process as everything else. From there, onboard one namespace at a time with destructive pruning disabled, watching until the reported state and the actual state agree for a full week before moving to the next. Wire a webhook so changes propagate quickly, but set a shared secret on the endpoint while you are there, because such endpoints generally lack rate limiting and an unauthenticated doorbell invites mischief. None of this needs a maintenance window. The reconciler reports differences before it fixes them, so the first week is mostly reading a calm dashboard and learning your own system&#8217;s habits. Assign one named owner for the rollout during this phase; shared ownership of a bootstrap is how weeks quietly become quarters.<\/p>\n<h2>Step 5: Put the drift policy in writing before the first disagreement<\/h2>\n<p>Drift is not one problem; it is four, and each class of resource deserves a pre-agreed response. Some differences you revert \u2014 an unlogged manual change to a service definition has no business surviving the next cycle. Some you tolerate, because fighting them is a losing battle: replica counts managed by an autoscaler, or fields filled in by admission webhooks, are the platform doing its job, and the honest configurations record that tolerance explicitly. Some you alert on without touching, where a human must judge. And some you protect from deletion outright \u2014 namespaces, persistent volumes, and resource definitions survive even an aggressive prune. Write the list down. On the night something drifts, nobody should be drafting policy at 2 a.m.; they should be reading it. The policy is short, the arguments are had once, in daylight, and the response becomes a lookup rather than a debate.<\/p>\n<h2>Step 6: Run the GitOps machinery like production, because it is<\/h2>\n<p>After adoption, the reconciler becomes one of the most critical pieces of software you operate, so treat it that way. Upgrade it deliberately and on a schedule, not when a dashboard turns amber. Rotate the keys that sign commits and decrypt secrets on a calendar, not on suspicion. Rehearse rebuilding a cluster from the store every quarter until the drill is dull \u2014 the first rehearsal will not be dull, which is the argument for having it early. Keep the build pipeline&#8217;s production credentials deleted; if some tool genuinely needs to write to a watched path, including any AI assistant somebody connected last month, it is now a deployer and gets scoped, reviewed access like every other deployer. And export the agent&#8217;s events for audit. Commit history records what people intended; reconciler events record what actually happened. Regulated teams need both.<\/p>\n<h2>Step 7: Measure two numbers and resist vanity dashboards<\/h2>\n<p>Exactly two metrics earn their place on the wall. The first is the time from merge to healthy \u2014 how long a reviewed change takes to become a correctly running system. Watch the median, but fear the tail: the p90 tells you how bad an ordinary bad day gets. The second is the count of changes reaching production outside the reviewed route. Every one of those is a hole in the model, and the number should fall month over month until it rounds to zero. Everything else \u2014 commit counts, sync frequencies, dashboard colours \u2014 is decoration until those two numbers move. When they do move, you will notice something quietly pleasant: releases stop being events. They become edits \u2014 unremarkable, frequent, and about as newsworthy as a normal ward round.<\/p>\n<h2>A closing word for definition-keepers<\/h2>\n<p>This site exists because shared, versioned definitions make clinical talk precise: the NYHA functional classes give cardiology a common scale, as the overview of <a href=\"https:\/\/heartcareforyou.in\/blog\/nyha-class-ii-definition-clinical-context-and-cardiology-overview\/\">NYHA Class II<\/a> shows, and grading conventions such as those in <a href=\"https:\/\/heartcareforyou.in\/blog\/heart-sounds-murmur-grading-definition-clinical-context-and-cardiology-overview\/\">heart sound and murmur grading<\/a> work the same way \u2014 one definition, many observers, no arguments about words. GitOps imports that discipline into how systems are run, and the runbook above is mostly an application of it. For the deeper material \u2014 tool comparisons, failure stories, and the reference layout behind step three \u2014 <a href=\"https:\/\/www.xopsschool.com\/gitops\/\">xOps School&#8217;s GitOps reference<\/a> is the practical next read, and the entry on <a href=\"https:\/\/heartcareforyou.in\/blog\/nyha-class-i-definition-clinical-context-and-cardiology-overview\/\">NYHA Class I<\/a> is a reminder that even the mildest classification deserves the same care as the severe ones. Declare the state you want. Keep the receipts. Let the machines do the fetching.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A plain GitOps adoption runbook in numbered steps: the day-zero decisions, a day-one bootstrap, repository layout rules, and the day-two habits that hold.<\/p>\n","protected":false},"author":5,"featured_media":741,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[392],"tags":[],"class_list":["post-740","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-healthcare-it"],"_links":{"self":[{"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/posts\/740","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/comments?post=740"}],"version-history":[{"count":1,"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/posts\/740\/revisions"}],"predecessor-version":[{"id":751,"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/posts\/740\/revisions\/751"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/media\/741"}],"wp:attachment":[{"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/media?parent=740"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/categories?post=740"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/heartcareforyou.in\/blog\/wp-json\/wp\/v2\/tags?post=740"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}