---
myst:
html_meta:
"description": "Scale the Plone backend and frontend with cdk8s-plone, configure a PodDisruptionBudget, and add a HorizontalPodAutoscaler."
"property=og:description": "Scale the Plone backend and frontend with cdk8s-plone, configure a PodDisruptionBudget, and add a HorizontalPodAutoscaler."
"property=og:title": "Scale and run highly available"
"keywords": "Plone, cdk8s, Kubernetes, scaling, replicas, PodDisruptionBudget, HPA, high availability"
---
# Scale and run highly available
This guide shows you how to run more than one replica of the Plone backend and frontend, protect them with a PodDisruptionBudget, and add a HorizontalPodAutoscaler.
## Prerequisites
- A working Plone deployment created with `cdk8s-plone`.
- A PostgreSQL backend reachable by every backend replica.
The backend uses RelStorage on PostgreSQL, so several backend replicas share one database and scale horizontally.
The Volto frontend is stateless and scales horizontally as well.
## Set the replica count
Set `replicas` on `backend` and `frontend` independently.
```typescript
import { Plone, PloneVariant } from '@bluedynamics/cdk8s-plone';
new Plone(chart, 'plone', {
variant: PloneVariant.VOLTO,
backend: {
image: 'plone/plone-backend:6.1.3',
replicas: 3,
},
frontend: {
image: 'plone/plone-frontend:16.0.0',
replicas: 2,
},
});
```
Both default to `2` replicas when you omit the option.
## Protect availability with a PodDisruptionBudget
A PodDisruptionBudget keeps a minimum number of pods running during voluntary disruptions such as node drains.
Set `minAvailable` or `maxUnavailable` on the component.
Each accepts an absolute number or a percentage string such as `"50%"`.
```typescript
backend: {
image: 'plone/plone-backend:6.1.3',
replicas: 5,
minAvailable: 3,
}
```
`cdk8s-plone` creates the PodDisruptionBudget automatically when `replicas` is `2` or more, or when you set `minAvailable` or `maxUnavailable` explicitly.
A single replica with no explicit setting gets no PodDisruptionBudget.
The generated PodDisruptionBudget always sets `unhealthyPodEvictionPolicy: AlwaysAllow`, so unhealthy pods never block a node drain.
## Add a HorizontalPodAutoscaler
`cdk8s-plone` emits plain Deployments and does not configure autoscaling.
To scale on load, add your own HorizontalPodAutoscaler that targets the generated backend Deployment.
Find the Deployment name from the synthesized manifests:
```shell
cdk8s synth
grep -A2 'kind: Deployment' dist/*.yaml | grep 'name:'
```
Add the autoscaler as a Kubernetes manifest and apply it alongside your deployment:
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: plone-backend
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name:
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
```
The HorizontalPodAutoscaler needs [metrics-server](https://github.com/kubernetes-sigs/metrics-server) in the cluster.
Keep `minReplicas` at `2` or more so the PodDisruptionBudget stays effective.
## Verify
Confirm the PodDisruptionBudget is present in the synthesized manifests:
```shell
cdk8s synth
grep -l 'kind: PodDisruptionBudget' dist/*.yaml
```
Inspect the running objects on the cluster:
```shell
kubectl get deploy -n
kubectl get pdb -n
kubectl get hpa -n
```
## See also
- {doc}`/reference/api/index` — authoritative `replicas`, `minAvailable`, and `maxUnavailable` reference.
- {doc}`/how-to/schedule-pods` — place the extra replicas on specific nodes.
- {doc}`/explanation/architecture` — how the backend, frontend, and database fit together.