Repository Structure
There are two ways a cluster’s files end up in a repository, and they can look different. The layout Ankra writes. When you connect a repository, or change a cluster in the portal, CLI, API or through Ankra’s AI, Ankra writes the cluster to its own directory:include paths:
AGENTS.md sibling files hold per-manifest and per-add-on operational learnings for your team and Ankra’s AI - see Teach Your Agents with AGENTS.md.
Cluster Definition Example
Yourimport-cluster.yaml references files using include paths:
manifests/ and addons/ folders. You can also include individual files for more control:
Single-File Cluster Definition
For small or self-contained setups you can skip include paths and define everything inline in one file. Manifest and values content is embedded as base64:parents to control deployment order - a resource only deploys after its parents succeed. Prefer include paths once a cluster definition grows beyond a couple of resources.
Every field of this file - stacks, manifests, add-ons, applications, parents, and encrypted paths - is documented in the ImportCluster YAML Schema.
Data Sources in the Cluster Document
The cluster’s metrics and log data sources live in the same file, asspec.prometheus_metrics and spec.log_source. Both are read back on every sync, so writing either block in Git connects the source and updates it - you do not have to configure it in the platform first:
username, password, token or auth_scheme inside a source block is refused, and the credential is referenced by credential_name instead. Deleting a block does not disconnect the source - Git can create and update one, only the platform can disconnect it. See spec.prometheus_metrics and spec.log_source.
How Ankra Syncs Your Cluster
- You update or add YAML files in your Git repository
- Ankra detects the change and automatically syncs your cluster
- All included manifests and add-ons are applied in the order you specify
What Ankra Writes Back to Git
Sync is two-way. Changes you commit are applied to the cluster, and changes made through the portal, the CLI, the API or Ankra AI are written back to the cluster’s directory in the repository, so Git stays the record. A platform write lands as a commit authored byankra-platform[bot] (a change made in the portal, CLI or API) or by Ankra Reconciler with the subject Sync cluster state from Ankra (the reconciler exporting stored state).
The ownership manifest: .ankra-owned.json
Every cluster directory carries a file named .ankra-owned.json. Ankra rewrites it on each platform write. It is a record of what that write rendered - not an inventory of the directory, and not a claim on it:
pathslists the files the last write rendered. It bounds deletion: when a stack or add-on is removed, only files listed here are pruned. A file Ankra never rendered is never deleted, only reported.contentsmaps each rendered file to a digest of the bytes Ankra itself last wrote there. It bounds overwriting: before writing a file, Ankra compares what is in Git with that digest. If they differ, the file was edited since, and Ankra keeps the Git version instead of replacing it.
Hand-maintaining a file
To keep a value Ankra would otherwise render - an image tag pinned above a chart’sappVersion for a CVE fix, an updateStrategy, topologySpreadConstraints - edit the file in Git and merge it. Ankra syncs the change into the cluster, and from then on the file’s bytes in Git no longer match the digest Ankra recorded, so no platform write replaces them. This holds for every write path: portal, CLI, API, Ankra AI and the reconciler’s own export.
Three limits apply today:
- SOPS-encrypted files are not covered. Their committed bytes are ciphertext that cannot be reproduced from the render, so Ankra keys them on the embedded content fingerprint instead, and re-encrypts the file whenever its stored values change. A hand edit to an encrypted file sealed under the cluster’s
.sops.yamlis imported like any other Git change and then re-sealed by Ankra’s next write - the values survive, the ciphertext does not, so expect one re-seal commit after rotating a sealed Secret by hand (see Rotating by API and SOPS). .sops.yamlin the cluster directory has its own rule. Ankra rewrites the creation rules it generates there on every export, but it no longer replaces the file: rules you add by hand are kept, and kept above Ankra’s own. See Merging the SOPS configuration file below.- Preservation is silent. Ankra logs
Preserving git content the platform did not writeon its side, but the sync status still reads in sync and no portal, CLI or API surface lists the preserved paths yet. While a file is preserved, a change made to it in Ankra does not reach Git - make the change in Git instead, which also brings Ankra’s copy back in line.
Merging the SOPS configuration file
The.sops.yaml in a cluster directory has two authors, so it is not covered by the digest boundary above: it is merged on every platform write instead of replaced, and the file it writes opens with a header saying so.
- Ankra owns only the rules it generates: a rule carrying a
path_regexAnkra generates, anagekey, and at most anencrypted_regex- and nothing else. Ankra rewrites those on every sync and lists them last in the file. While the cluster declaresencrypted_paths, each generated rule carries anencrypted_regexbuilt from the union of every declaration across the cluster’s stacks, rebuilt on every sync as the declarations change, so asops -ethat applies the rule seals only declared secret keys and leaves every other key readable. It is one union per cluster, so it is broader per file than what Ankra itself seals per resource: a key declared secret for any resource in the cluster is sealed in every matching file. - Every other rule is yours. It is preserved verbatim and kept above Ankra’s own. SOPS applies the first matching creation rule, so a rule of yours takes precedence over Ankra’s - including a deliberately keyless first rule, which is the supported way to keep a path out of encryption and make an external
sops -eon it fail loudly rather than seal a file that must stay plaintext. Write itspath_regexrelative to the cluster directory, not the repository root:sopsmatches a rule against the file’s path relative to the directory holding the.sops.yamlit is using, so for the cluster’s file the path being tested isstacks/<stack>/add-ons/<add-on>/values.yaml. A rule written from the repository root never matches, and a refusal caused by nothing matching is an accident, not protection - see Checking that a keyless rule applies. - To override one of Ankra’s rules, write a rule that is not shaped like Ankra’s: keyless, using
key_groups, carrying a field other thanpath_regex,ageandencrypted_regex, or on a differentpath_regex. A rule that is apath_regexAnkra generates plus anagekey, with or without anencrypted_regex, cannot be told apart from Ankra’s own output and is replaced whatever itsageorencrypted_regexvalues are - that is what lets a key rotation or a changed declaration reach the file, and it happens silently. To keep your own recipients or your ownencrypted_regexon a path Ankra covers, name the recipients withkey_groups. The warning below spells it out. - Other top-level keys are yours too.
stores,kms,shamir_thresholdand anything else in the file are carried through untouched. - Ankra does not read these rules. Its own encryption is driven per resource by that resource’s
encrypted_pathsdeclaration, compiled to a SOPSencrypted_regexwhen the resource is sealed (see SOPS). The file exists for external tooling that runssopsagainst the repository.
.sops.yaml Ankra cannot parse is left exactly as it is, rather than rewritten.
Checking that a keyless rule applies
sops looks for .sops.yaml by walking up from the current directory, not from the file it is given, and matches each path_regex against the file’s path relative to the directory holding that .sops.yaml. Test a rule from the repository root with an explicit --config, writing to stdout (never -i):
Pair the check with a positive control in the same run: a neighbouring file that only a rule with recipients covers must encrypt, or the refusal proves nothing about your rule. Ankra’s generated rules are written relative to the cluster directory (
.*values\.yaml$ and .*manifests/.*\.yaml$), so on a cluster synced since that change they match and can serve as the control: a values file only they cover must encrypt. A .sops.yaml still carrying the older repo-rooted spellings (<cluster-directory>/.*values\.yaml$) predates the change - those rules match nothing, Ankra’s next write to the cluster replaces them, and until then a control needs a keyed rule of your own, written relative to the cluster directory like the keyless one.
When Git and the Platform Both Change
Because sync is two-way, the same resource can change on both sides between two syncs: someone edits a stack in the portal while a commit to the same stack lands in Git. Ankra does not guess which side you meant. On every sync Ankra compares three things: the head of the branch, the state stored in the platform, and the baseline both sides last agreed on. That comparison has four outcomes:
A conflict pins the cluster’s sync. Nothing further is applied from Git and nothing further is pushed to it until every conflicting resource has a decision. For each one you choose Git (the committed version wins and the platform change is dropped) or Cluster (the platform version wins and is committed over the Git version). Resolve conflicts from the GitOps section of the cluster settings, one resource at a time or all at once, or with the
list_gitops_conflicts, get_gitops_conflict_diff and resolve_gitops_conflict MCP tools. Sync resumes on its own once the last conflict is decided.
There is no setting that makes one side win automatically. If Git should be the authority for a cluster, make changes to that cluster only through Git: with no platform-side edits there is nothing to conflict with. If a platform write finds a commit in Git that Ankra has not imported yet, the change is saved in the platform but its push to Git is held back, and the next sync merges the two sides or raises a conflict as above.
Connecting a repository to a cluster that already has state
When you connect a repository to a cluster Ankra is already managing, Ankra first pushes the cluster’s current state to the repository as a commit namedConnect GitOps repository, then starts syncing. Connect an empty repository, or one Ankra has written to before. A repository that already holds files at the paths Ankra renders, and has no .ankra-owned.json, has no overwrite boundary yet, so that first push can replace those files.
You do not need a repository at all. With the native engine, Ankra reconciles stacks and add-ons from the state stored in the platform, and a cluster with no repository connected is fully supported. Connecting one adds Git as a second place to read and make changes; it does not change how the cluster is reconciled.
Monitoring Sync Status
Track the status of your GitOps syncs in the cluster settings.Sync Status Banner
Navigate to your cluster → Settings → GitOps to see the current sync status:Sync Progress
During a sync, you’ll see the progress through these phases:- Fetching - Pulling latest changes from Git
- Validating - Checking YAML syntax and configuration
- Applying - Deploying changes to the cluster
- Completed - Sync finished successfully
Manual Sync
Click Sync Now to trigger an immediate sync from your Git repository. Use this when:- You want to apply changes without waiting for automatic detection
- Webhook delivery failed
- You need to force a refresh
Sync History
View a complete history of all GitOps syncs for your cluster.Accessing Sync History
- Go to your cluster → Settings → GitOps
- Scroll down to the Sync History table
History Table Features
The sync history table shows:
Table Features:
- Pagination: Navigate through history (10 entries per page)
- Sorting: Click column headers to sort by any field
- Commit Links: Click the SHA to view the commit in GitHub
Sync Metadata
Each sync entry includes detailed metadata:- Last Commit SHA: The exact commit that was synced
- Sync Source:
webhook(automatic) ormanual - Timestamp: When the sync occurred
- Retry Count: Number of retry attempts (if any)
- IAC Files Link: Direct link to view the configuration files in your repository
Handling Sync Errors
When a sync fails, Ankra provides detailed error information to help you fix issues quickly.Error Display
Failed syncs show:- Validation Errors: Issues with YAML syntax or configuration
- Field-Level Guidance: Specific fields that need attention
- Missing Fields: Required fields that weren’t provided
- Typo Detection: Suggestions for misspelled field names
Common Error Types
Validation Errors
Validation Errors
YAML syntax issues or invalid configuration values.Solution: Check the error details for the specific field and fix the YAML in your repository.
Missing Required Fields
Missing Required Fields
Required configuration fields weren’t provided.Solution: The error shows which fields are missing with examples of expected values.
Resource Conflicts
Resource Conflicts
A resource already exists or conflicts with another definition.Solution: Check for duplicate resource names or conflicting configurations.
Retrying Failed Syncs
- Fix the issues in your Git repository
- Commit and push the changes
- Click Sync Now or wait for automatic detection
- Monitor the new sync in the history table
Troubleshooting
If Ankra isn’t syncing as expected:Related
- Stacks - Learn about organizing resources into stacks
- Add-ons - Install Helm charts as add-ons
- Manifests - Deploy your own Kubernetes YAML
Still have questions? Join our Slack community and we’ll help out.