Many teams keep application source code in one repository and its Kubernetes manifests in a separate "config" repository. Why split them for GitOps delivery, and what does the split cost?
answer
- different writers, different lifecycles
- the build commits the tag somewhere
- a commit that triggers itself
- one code branch, many environments
- atomicity is what you pay
basics
~20 sSeparating manifests from source keeps automated version bumps from re-triggering builds, lets deployment changes be reviewed and permissioned differently from code, and lets one app deploy to many environments. The cost is that a single logical change now spans two repositories and two pull requests.
solid answer
~50 sThe split exists because the two repositories have different writers and different lifecycles. The build pipeline **writes** to the config repository — it commits the new image reference after a successful build — and if that commit landed in the source repository it would re-trigger CI, so teams need commit loops broken or `[skip ci]` conventions. The config repository also has a different audience: production manifests can carry stricter reviewers and branch protection than application code, and the same application often has several environment directories that no single code branch maps onto. What you pay is atomicity and traceability: one logical change becomes two pull requests, and answering "which config is running this code" requires a convention linking the two commits. Many teams take the middle road — one repository, separate directories, path-scoped CI and reviewers.
code
bash · 13 lines#!/usr/bin/env bash
set -euo pipefail
DIGEST="$1" # sha256:... of the image just pushed
SRC_SHA="$(git rev-parse HEAD)"
git clone --depth 1 [email protected]:acme/platform-manifests.git cfg
cd cfg/overlays/staging
kustomize edit set image \
registry.example.com/checkout="registry.example.com/checkout@${DIGEST}"
cd ../..
git commit -am "checkout: staging -> ${DIGEST} (src ${SRC_SHA})"
git push origin maingo deeper
Know that in this model a merged pull request in the application repository does not deploy anything on its own; a second change to the manifests is what triggers the deployment.
Explain the concrete drivers — the pipeline writes the image reference, review and permissions differ, and one version fans out to several environments — and name the atomicity you give up.
Show you can pick per team rather than by dogma, and design the traceability convention that ties an image digest back to a config commit and a source commit for incident response.
Own the boundary question across an organization: how many config repositories, who can write to a production path, how bootstrap and access review scale, and what conventions keep an automated commit stream reviewable rather than rubber-stamped.
## Two repositories because there are two writers The cleanest way to think about the split is to ask who commits. Application source is written by humans in feature branches. The manifest repository is written mostly by **automation**: after a successful build, the pipeline commits a new image reference into an environment directory. Those are different write patterns, with different review needs and different noise levels, and mixing them into one history is what causes most of the friction teams then work around. ## What the split actually buys **It breaks the CI feedback loop.** If the pipeline commits the new image tag back into the same repository whose pushes trigger the pipeline, you get a build that triggers a build. The workarounds are all ugly — a `[skip ci]` marker in the commit message, path filters excluding the manifest directory, a bot account excluded from triggers. With a separate config repository the loop simply does not exist: pushes there are watched by the delivery agent, not by the build. **It separates permissions from code review.** Reviewing a change to a payment service's business logic and reviewing a change to production replica counts, network policy or image version are different judgments, often made by different people. A separate repository lets you give the production path its own reviewers and its own protection rules without contorting the source repository's rules. **It matches the fan-out.** One application version is usually deployed several times — dev, staging, production, per-region, per-tenant. The source repository has one `main` and a linear version history; the config repository has one directory per deployment target, each pinned to a possibly different version. Those shapes do not line up, and forcing them into one repository tends to produce environment branches, which is a worse problem. **It keeps the agent's view small.** The delivery agent clones and re-reads the repository it watches. A config repository is small text; an application repository may be gigabytes with a long history. Watching the small one is cheaper and its commit stream is meaningful — every commit is a deployment intent. ## What the split costs **Atomicity.** A change that needs both a code change and a manifest change — a new environment variable, a new port, a changed health-check path — can no longer be one reviewable commit. It becomes two pull requests with an implicit ordering, and getting the order wrong produces a CrashLooping Pod or a config key nothing reads. **Traceability.** "What manifests are running with commit `abc123` of the service?" is trivially answerable in one repository and needs a convention across two. The usual fix is to make the promotion commit message carry the source commit SHA and the image digest, so the config history is a searchable ledger of what came from where. **Cognitive overhead.** New engineers must learn that the deployment lives somewhere else, and that a merged pull request in the source repository has not deployed anything. **Two things to secure, back up and bootstrap.** The config repository is now a production dependency in its own right: whoever can write to its production path can change production. ## The middle road: one repository, separate directories A monorepo with `src/` and `deploy/` can get most of the benefits if you are deliberate: point the agent at the `deploy/` path only, scope CI triggers with path filters so manifest commits do not rebuild, and use code-owner rules so the production path needs different approval. This keeps a code-plus-manifest change atomic, which is genuinely valuable for a small team owning one service. It becomes untenable when many services share one delivery repository, or when the code repository's access control cannot be narrowed enough for production manifests. ## How the bump reaches the config repository In practice the pipeline either clones the config repository and pushes a commit (or opens a pull request) using a narrowly-scoped deploy key, or an image-automation controller watches the registry and writes the commit itself. Either way, the important properties are the same: the write is scoped to one path, the commit says which source revision it came from, and the production path requires review rather than accepting an automated push. ## What a good answer sounds like Do not present the split as an unconditional best practice. Say what problem it solves for the team in front of you — CI loops, permission separation, environment fan-out — and be honest that a single service with one environment and one team is often better served by one repository with disciplined path scoping.
- If both live in one repository, what specifically prevents the tag-bump commit from re-triggering the build?Path-scoped triggers: the build pipeline ignores pushes that touch only the deployment directory, and the delivery agent watches only that directory. Some teams additionally exclude the automation account or use a skip marker in the commit message, but path scoping is the robust version because it does not depend on message conventions anyone can forget.
- How do you answer "which source commit produced what production runs" when the two repositories are separate?Make the promotion commit carry it: the message or a small metadata file records the source repository commit SHA and the image digest, and the image itself gets a label with the same SHA. Then production's current digest resolves to a config commit, which resolves to a source commit — a chain you can walk during an incident without guessing.
- Should each service get its own config repository, or should one repository serve them all?One repository per delivery domain — a team or a platform — usually wins: fewer places to bootstrap, one place to see what a cluster runs, and shared conventions. Split it when review or access boundaries genuinely differ, when the commit volume drowns reviewers, or when a tenant must not be able to read another's manifests.
saying these in an interview costs you the question
- Claims the split is mandatory for GitOps to work
- Cannot say who writes the config repository
- Ignores that a bot commit can retrigger CI
- Thinks merging the source PR deploys the service
- Treats two repositories as free — no traceability convention