How big is a workload — size classes
What small, medium, large and xlarge mean, why they are different absolute numbers for a Rails service and a static site, and where to set one.
The one thing to know first
A size class is relative to the component type, not absolute. small is each
type's own baseline — what the platform thinks a workload of that shape needs for
a single user and job traffic — and every step above it doubles:
| class | multiplier |
|---|---|
small |
the type's baseline |
medium |
×2 |
large |
×4 |
xlarge |
×8 |
So a Rails large and a Go large are different absolute numbers, and both mean
four times the platform standard for this kind of workload.
The alternative — one absolute table for every type — was rejected because it
cannot be right for both ends of the range. A number generous enough for a Rails
service with a db:prepare init container is eight times too big for a static
site serving files out of nginx.
What small actually is, per type
Memory request / limit at small. Everything above doubles from here.
| component type | small |
|---|---|
static-site |
64Mi / 128Mi |
go-service |
64Mi / 256Mi |
grpc-service |
128Mi / 320Mi |
python-service |
128Mi / 768Mi |
zulip-bot |
128Mi / 768Mi |
ruby-service |
256Mi / 768Mi |
ruby-scheduled-task |
256Mi / 768Mi |
ruby-worker |
512Mi / 768Mi |
vendored-service |
1536Mi / 1536Mi |
CPU follows the same ladder. Each type's exact numbers are on its own
construct page under environmentConfigs.sizeClass, and they are written into
the ComponentType as literal quantities rather than computed in CEL — so
the ladder is readable in the YAML, on the construct page, and in a manifest
diff.
Where you set one
On the ReleaseBinding, per environment. That is the whole point of it being there rather than on the Component: production can be bigger than staging without cutting a second release, and the two environments keep running the same immutable ComponentRelease.
apiVersion: openchoreo.dev/v1alpha1
kind: ReleaseBinding
metadata:
name: inkwell-worker-production
spec:
owner: {projectName: inkwell, componentName: inkwell-worker}
environment: production
componentTypeEnvironmentConfigs:
sizeClass: medium
It lives in the application repository, in openchoreo/bindings-<environment>.yaml,
and reaches the cluster on the next push. How big a workload is in production is
a property of the application, belongs beside its code, and should be reviewed as
a diff.
small chosen and small by default are different facts
The console renders them differently, and this is deliberate. An outlined chip means somebody declared that class on this binding; a flat, faint chip means nobody has said anything and the type's default applies. They are the same word and a different fact: one is a decision, and a decision can be revisited.
The Size column on any environment. A dim chip is a default; a bright one is a choice.
See the sizes per environment →Overriding it outright
An explicit resources block on the binding wins over the class entirely, and
the component then renders as custom rather than as any class:
componentTypeEnvironmentConfigs:
resources:
requests: {cpu: 500m, memory: 700Mi}
limits: {cpu: 1, memory: 2Gi}
Prefer a class. custom cannot be compared across environments, does not appear
in the ladder, and is the reason the size chip has a fourth state.
The cell quota is the real ceiling
Each project cell has a ResourceQuota capping the sum of its containers'
memory limits — 6Gi on this platform. A size class is charged against it, and a
promotion is refused before it starts if the projection does not fit
(platform/project-flow capacity_check).
Two pieces of arithmetic bite here and both are easy to get wrong by hand.
Kubernetes charges a pod max(sum(containers), max(initContainers)), not the
sum of the two — so a Rails pod's 768Mi db-prepare init container contributes
nothing on top of the containers that follow it.
And a rollout surges: maxSurge rounds up to 1, maxUnavailable rounds down
to 0, so during a resize the cell holds the old pod at its old size and the new
pod at its new one at the same time.
That pair is why inkwell-worker is medium in production and not large:
production already holds about 2.7Gi across five components, and large plus the
surge does not fit under 6Gi.
What a resize does
It changes the pod template, so it rolls the pods — which resizing a container
does anyway. The class is stamped onto the pod template as
platform.jung.town/size-class, as an annotation rather than a label,
because spec.selector.matchLabels is immutable and a generated label in the
selector is a way to make a Deployment unpatchable.
The stamp is what lets a cross-environment comparison render a size difference as
one named line — small → medium — instead of four numeric ones with no
explanation.
Open a project, then Compare. A size difference shows as the class it is.
Compare two environments →