The TYPO3 backend gets a pod of its own
For anyone running TYPO3 on Kubernetes where editors and visitors share the same pod. The idea is older than any savings plan: the editors' load should not slow down the website, and vice versa. Now the backend has a Deployment of its own, and both sides answer faster. This field note shows the rebuild, the order of the switch and the first measurements. The fast pod start from the field note on the pod start in twelve seconds made it possible.
01 — One pod for everything
Until now a single Deployment served all hosts of a tenant. That included the backend host the editors work on. It has a name of its own and an IP allow list in the edge proxy, but it landed in the same pod as the website. FrankenPHP serves the website in worker mode and the backend classically. Both still shared the same CPU request and the same PHP threads.
The two sides load a pod very differently. The website gets many short requests, and a large share of them comes from the edge cache anyway. The backend gets fewer requests, but heavy ones: list views, modules, AJAX and the MCP calls of an AI assistant. When editors click through the backend while visitors and crawlers fetch the website, each waits for the other.
That is exactly what the split is meant to solve. The idea behind it is simple: whatever loads differently gets a pod of its own.
02 — A second Deployment from the same template
The backend needs neither code nor an image of its own. The chart now renders the app's pod template twice: once as app and, when a switch is set, a second time as app-backend. The copies differ in exactly three things. Name, component label and replica count.
app:
backend:
split: true
scaleToZero:
enabled: false # only after a week in production
Both Deployments run under the same ServiceAccount and therefore have the same pod identity. The certificate still carries the name app, and the edge proxy checks against that name or the request's host, never against the address it dials. No permission had to change owners.
The network rules took more work. In the namespace, components may only talk to each other the way a rule allows. app-backend needs exactly the app's paths: to the database, to the cache, from the edge proxy. The chart derives them from the app's list instead of writing them down a second time. A CI check compares both sides rule by rule and is demonstrably red as soon as a single rule is missing.
That leaves the namespace quota. A second app-sized pod plus its surge pod in a rollout no longer fitted into 8 GiB of memory limits. Counted against the worst case it is now 10 GiB, with a documented deviation in the tenant.
03 — The edge proxy switches
Which pod serves the backend host is the edge proxy's decision. Its Caddyfile got an upstream of its own for that host. Every variable in it falls back to the value the rest already uses. An image with this change therefore behaves exactly like one without it until the chart sets the variables.
(backend-upstream) {
reverse_proxy {$BACKEND_UPSTREAM_HOST:app}:443 {
dynamic a {
name {$BACKEND_UPSTREAM_DYNAMIC_HOST:app-headless}
port 8443
refresh 10s
}
lb_try_duration {$BACKEND_LB_TRY_DURATION:5s}
lb_try_interval 250ms
}
}
That allowed an order without risk. The chart came first: app-backend ran but got no traffic yet, because the old proxy image ignored the new variables. In that window the pod could be checked directly. Login page, stylesheets and scripts answered as they do in the app. Only then came the new proxy image, and with its restart the backend host went to app-backend. The platform follows the same rule as with the pod certificates: grant first, switch second.
lb_try_duration is still short today. Once the backend sits at zero pods, the proxy uses exactly this value to hold a request until a pod is ready, instead of answering with an error.
04 — What the split brings
The first impression after the switch was clear: the backend felt faster than ever, and so did the website. The edge proxy measures the response time per host, and the first hour confirms the impression.
| Mean response time per request | Backend host | Website |
|---|---|---|
| Two days before | 257 ms | 277 ms |
| The day before | 64 ms | 115 ms |
| The hour before the switch | 160 ms | 104 ms |
| The hour after | 25 ms | 79 ms |
The numbers need context. They are means over all requests of an hour, and the mix of requests changes them a lot. Clicking through the backend produces many small requests for stylesheets and AJAX, and those pull the mean down. Even so, the order of magnitude for the backend is clear; for the website it is about a quarter.
05 — Why the split makes things faster
Which share goes to which cause has not been measured separately. Three causes are plausible, and the first probably weighs most.
- No more sharing: a backend request had to wait when the website was busy, and vice versa. Now each side has its own CPU share and its own PHP threads. Editors no longer compete with visitors, crawlers and probes.
- Warm caches stay warm: since the move to setup hooks, no pod start empties the caches any more. That was true before, but it matters more with two pods.
- A smaller working set per pod: the backend pod's opcode cache and realpath cache only hold backend code. The website's paths no longer push it out.
Both pods are barely loaded, about 0.015 CPU cores each at rest. The difference shows above all when both sides work at the same time. A week of daily comparison will show how much of it stays.
06 — Scaling independently, in both directions
Two Deployments can be scaled separately. The website's Horizontal Pod Autoscaler now sees only the visitors' load. A busy editorial session no longer triggers an extra website pod, and a rush on the website no longer takes compute time away from the editors.
The backend can get an HPA of its own, and it may work in both directions. Upwards, when many editors work at the same time or an AI assistant sends a series of heavy requests over MCP. Today the chart still caps the backend at one pod. More is a value, not a rebuild.
And downwards to zero. Nobody works in the backend at night or at the weekend, and a pod that runs anyway holds memory and attack surface nobody needs. Since Kubernetes 1.37 a Horizontal Pod Autoscaler can go to zero replicas. It then needs a metric that exists without a running pod. The edge proxy, which always runs, provides it: requests per host, through a Prometheus adapter as an external metric.
Two measurements set the frame. In a test the HPA went from zero to one pod 17 seconds after load started, and a pod is ready after twelve seconds. An editor who logs in first in the morning therefore waits about half a minute. The proxy holds her request for that long instead of answering with an error.
| Step | Duration |
|---|---|
| Request arrives, metric reaches the HPA | about 17 s |
| Pod starts and becomes ready | about 12 s |
| Hold time in the proxy | up to 90 s |
The savings are a pleasant extra, not the reason for the split. It gets switched on only once editors have worked with the separate backend for a week without problems.
Frequently asked questions
Doesn't a second Deployment cost more resources?+
One more pod during the day, with 400 MiB of memory and 300m of CPU requested. The namespace quota was raised explicitly for it. In return each side scales only with its own load, and at night the backend can go to zero. Then the tenant ties up less than before.
What happens to sessions and the preview?+
The hosts stay the same, and so do the cookies. Backend sessions live in Valkey, not in the pod, and both Deployments read the same ones. The preview still runs through the website's host.
Does the backend need its own code or image?+
No. Both Deployments mount the same signed image, pinned to the same digest. The split happens only in the edge proxy, which sends the backend host to a different pod. FrankenPHP already tells website and backend apart by host.
Conclusion
Editors and visitors have different load profiles. As long as they share a pod, they slow each other down, and no autoscaler can tell them apart. Separated, both answer faster, and each side scales with its own load: the website with the visitors, the backend with the editors, at night down to zero.
The rebuild itself was small: the same pod template twice, derived network rules, a second upstream in the edge proxy. The work was in the order. The new pod ran and had been checked before a single request reached it.
Do editors and visitors share a pod in your setup?
I look at how the backend and the website of your TYPO3 instance share resources, and split them so that both answer faster and the backend no longer ties up resources outside working hours.
About the author

Kai Ole Hartwig
Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.