Too Long, Didn't Apply: Server-Side Apply for Oversized CRDs in Argo CD ApplicationSets

TemplatePatch is one of Argo CD's lesser-known features. It is also the cleanest way to enable server-side apply for exactly one generated Application - and to stop oversized CRDs from failing to sync.

Too Long, Didn't Apply: Server-Side Apply for Oversized CRDs in Argo CD ApplicationSets
Created by ChatGPT

When dealing with big resources in Argo CD - especially with Custom Resource Definitions (CRDs) - you will regularly see an error that these resources cannot be synchronized. The error message looks something like this:

error when patching "/dev/shm/1487475670": CustomResourceDefinition.apiextensions.k8s.io "httproutes.gateway.networking.k8s.io" is invalid: metadata.annotations: Too long: may not be more than 262144 bytes

The root cause is the default synchronization behaviour of Argo CD. It uses client-side apply, which writes the full serialized manifest into the kubectl.kubernetes.io/last-applied-configuration annotation. The problem with this approach is the fact that annotations may not exceed a specific size in Kubernetes - 262144 bytes to be precise.

A football shirt can prompt questions about a club, a season, or a remembered match, yet its appearance alone rarely answers them. A collector can begin with direct observations: colours, badge placement, collar shape, stitching, and any visible labels. Those details describe the object; they do not automatically establish when it was made or who wore it. A product described as retro might be an old garment, a later reissue, or a contemporary design inspired by the past. Keeping those possibilities distinct makes a catalogue more useful than a confident story built from visual resemblance.

A reference such as equipación clásica fútbol histórico(equipación clásica fútbol histórico) can lead to further reading, but each historical statement should be tied to a source that identifies the same model. Record the source title, access date, and the detail it supports. If a season is not documented, leave it unconfirmed rather than filling the gap from memory. Compare photographs only when they appear to show the same version; a cropped image may hide the differences needed for identification. A seller’s wording can provide a research lead, but it should remain labelled as a listing claim until another reference supports it.

A modest record can still preserve meaningful context. Photograph the front and relevant details, note the garment’s observed condition, and separate personal memories from information found in an archive or publication. When sources disagree, keep both references and explain the conflict instead of choosing the more dramatic account. This approach does not diminish the emotional value of a shirt; it protects that value from being confused with an unverified historical attribution. Whether a collection contains one cherished item or many examples, transparent notes make later comparisons easier and let other fans understand what is known, what is inferred, and what remains open.

The fix for this problem is rather easy: use server-side apply (SSA) for the affected Application. SSA records ownership in metadata.managedFields, so nothing ever lands in annotations and the limit doesn't apply. To change the synchronization behaviour you only have to adjust the syncOptions of your Application like this:

spec:
  syncPolicy:
    syncOptions:
      - Validate=true
      # The magic fix
      - ServerSideApply=true

This settings uses ServerSideApply for every resource that is part of this Application. If you want to adjust the synchronisation behaviour only for the resources that are actually too big you can also an annotation, like this:

metadata:
  annotations:
    argocd.argoproj.io/sync-options: ServerSideApply=true

If you only need to adjust a single resource of Application the required changes are quite easy to implement, but what about an ApplicationSet? You usually don't want to activate SSA for every Application that has been created by an ApplicationSet, which would happen if you add the syncOption to it's specification. In such a situation, you can use an Argo CD feature that is still not very widely known: templatePatch.

TemplatePatch to the rescue

With TemplatePatch you can define more complex logic like loops or conditions that will be added as a patch on your template. In this scenario we can use it to only add the ServerSideApply flag on the desired Application. In the example I am assuming a Git Generator and want to activate ServerSideApply when the last folder equals gateway-api-crds:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  ...
spec:
  goTemplate: true
  goTemplateOptions:
    - missingkey=error
  generators:
    ...
  template:
    ...
  templatePatch: |
    {{`{{ if eq .path.basename "gateway-api-crds" }}`}}
    spec:
      syncPolicy:
        syncOptions:
          - Validate=true
          - ServerSideApply=true
    {{`{{ end }}`}}

In general you can patch everything you want with a TemplatePatch, except the spec.project field. This feature also requires that you enable goTemplate for the ApplicationSet. The patch is inert without it.