Summary
The Plone construct in cdk8s-plone renders a Kubernetes Deployment for the Plone backend with the default Kubernetes rolling strategy (maxSurge: 25%, maxUnavailable: 25%). For small replica counts + slow container startup (Plone is ~30-60s to "Ready"), this allows a measurable capacity dip during rollouts. There's no way to override it through the construct's API — users need to reach inside with ApiObject.of(...).addJsonPatch(...) to tune it.
Why it matters
A typical production rollout under the defaults:
replicas: 6, maxSurge: 25% → 1-2 extra pods allowed during rollout
maxUnavailable: 25% → 1-2 pods allowed down during rollout
- Plone readiness takes 30-60s; during that window, ready-pod-count drops from 6 to ~4
Combined with a cache layer in front (Varnish), URLs that are never cached (authenticated, POST, uncacheable responses, rare querystring combinations, newly-hashed asset URLs) hit the reduced-capacity backend and see 503s for the rollout's duration. Varnish grace mode covers cached content but can't rescue the cold-request case.
For teams that want zero-capacity-dip rollouts the standard fix is maxUnavailable: 0, maxSurge: 50% (or 100%) — new pods come up fully before old ones retire. The only cost is a temporary overshoot of pod count (and thus memory/CPU scheduling) during the rollout, which is acceptable for most production clusters with headroom.
Current workaround (escape hatch)
import { ApiObject, JsonPatch } from 'cdk8s';
import * as k8s from './imports/k8s';
const plone = new Plone(this, 'plone', { ... });
const backendDeployment = plone.node.findAll().find(c =>
c instanceof k8s.KubeDeployment && c.node.id.includes('backend')
);
if (backendDeployment) {
ApiObject.of(backendDeployment).addJsonPatch(
JsonPatch.replace('/spec/strategy', {
type: 'RollingUpdate',
rollingUpdate: { maxUnavailable: 0, maxSurge: '50%' },
}),
);
}
Brittle: depends on node.id.includes('backend') finding the right construct, and bypasses type-safety. Every consumer that needs this rebuilds the same workaround.
Proposed API
Add a rollingUpdate (or strategy) option to the backend config in PloneBackendConfig (or wherever pod-level settings live for the backend):
export interface PloneBackendConfig {
// ... existing fields ...
/**
* Rolling update strategy for the backend Deployment.
* Defaults to Kubernetes defaults (25% surge / 25% unavailable).
*
* For zero-capacity-dip rollouts use `{ maxUnavailable: 0, maxSurge: '50%' }`.
*/
readonly rollingUpdate?: {
readonly maxSurge?: number | string;
readonly maxUnavailable?: number | string;
};
}
Passed straight through to Deployment.spec.strategy.rollingUpdate, with type: 'RollingUpdate' implied.
Alternative more-explicit form: accept the whole DeploymentStrategy object if someone wants type: 'Recreate' (rare but valid for dev/test).
Either shape is a one-line map to the Deployment resource, so the implementation is a small change.
Same thing for the other Deployments
If the construct also emits Deployments for related workloads (httpcache, pgthumbor, tika, tika-worker) the same knob would apply. Could either:
- Mirror the field on each per-role config block (repetitive but explicit), or
- Have a single
defaultRollingUpdate plus per-role overrides.
For aaf-deployment we would realistically want it on backend and on httpcache (Varnish also ~10s to fully warm up + re-learn cluster peers). Rest are low-traffic enough that default is fine.
Happy to send a PR
If the API shape is acceptable I can prepare a PR — the change is localized and the tests should be straightforward (snapshot the Deployment spec and assert the strategy section).
Summary
The
Ploneconstruct incdk8s-plonerenders a KubernetesDeploymentfor the Plone backend with the default Kubernetes rolling strategy (maxSurge: 25%,maxUnavailable: 25%). For small replica counts + slow container startup (Plone is ~30-60s to "Ready"), this allows a measurable capacity dip during rollouts. There's no way to override it through the construct's API — users need to reach inside withApiObject.of(...).addJsonPatch(...)to tune it.Why it matters
A typical production rollout under the defaults:
replicas: 6,maxSurge: 25%→ 1-2 extra pods allowed during rolloutmaxUnavailable: 25%→ 1-2 pods allowed down during rolloutCombined with a cache layer in front (Varnish), URLs that are never cached (authenticated, POST, uncacheable responses, rare querystring combinations, newly-hashed asset URLs) hit the reduced-capacity backend and see 503s for the rollout's duration. Varnish grace mode covers cached content but can't rescue the cold-request case.
For teams that want zero-capacity-dip rollouts the standard fix is
maxUnavailable: 0, maxSurge: 50%(or100%) — new pods come up fully before old ones retire. The only cost is a temporary overshoot of pod count (and thus memory/CPU scheduling) during the rollout, which is acceptable for most production clusters with headroom.Current workaround (escape hatch)
Brittle: depends on
node.id.includes('backend')finding the right construct, and bypasses type-safety. Every consumer that needs this rebuilds the same workaround.Proposed API
Add a
rollingUpdate(orstrategy) option to the backend config inPloneBackendConfig(or wherever pod-level settings live for the backend):Passed straight through to
Deployment.spec.strategy.rollingUpdate, withtype: 'RollingUpdate'implied.Alternative more-explicit form: accept the whole
DeploymentStrategyobject if someone wantstype: 'Recreate'(rare but valid for dev/test).Either shape is a one-line map to the Deployment resource, so the implementation is a small change.
Same thing for the other Deployments
If the construct also emits Deployments for related workloads (httpcache, pgthumbor, tika, tika-worker) the same knob would apply. Could either:
defaultRollingUpdateplus per-role overrides.For aaf-deployment we would realistically want it on
backendand onhttpcache(Varnish also ~10s to fully warm up + re-learn cluster peers). Rest are low-traffic enough that default is fine.Happy to send a PR
If the API shape is acceptable I can prepare a PR — the change is localized and the tests should be straightforward (snapshot the Deployment spec and assert the
strategysection).