Skip to main content

Buildpacks RFC 329 Would Make `web_worker` an Illegal Process Type — and That's the Point

8 min readDora NodaDora Noda
Share
On this page

Static_Assets is a perfectly legal Cloud Native Buildpacks process type today. It is also not a valid DNS name, not a valid Kubernetes resource name, and not routable as a subdomain — everywhere the process type actually lands after the build, it is rejected. That gap between what the spec permits and what the world accepts is the entire subject of buildpacks/rfcs#329, "DNS Label Compatible Process Types": opened in March 2025, marked ready for review in July 2026, approved by Joe Kutner later that month, and still open at the time of writing.

The proposal is small and sharp: process types must become lowercase RFC 1123 DNS labels. Underscores out, periods out, uppercase out, leading and trailing dashes out, and fewer than 63 characters. This post covers the exact rule, the rejected alternative most summaries get wrong, why the constraint belongs in the spec rather than in every platform's private slugifier, and the migration bill for process types that already shipped.

The rule, exactly

The current buildpack specification restricts process types (the type identifier for a launch process in launch.toml) with one line:

MUST only contain numbers, letters, and the characters ., _, and -.

That permits web, worker, api-v2 — and also Static_Assets, webWorker, billing.cron, and -weird-. The RFC would replace it with the DNS label rule:

Current specRFC 329 proposal
LettersUpper and lowerLowercase only
DigitsYesYes
Hyphen -Yes, anywhereYes, except first or last character
Underscore _YesNo
Period .YesNo
Max lengthUnboundedFewer than 63 characters

So web-worker survives untouched. web_worker dies on the underscore. webWorker dies on the uppercase W. billing.cron dies on the period. And a 200-character generated type dies on length. The platform side changes to match: platforms would reject non-compliant process types with an explanatory error, something like telling the author that Static_Assets must comply with lowercased DNS label syntax because underscores and uppercase letters are not allowed.

The lowercase requirement deserves a sentence. DNS itself is case-insensitive — API and api are the same name — which is precisely the problem: two casings that look different in launch.toml collide into one DNS entry or one Kubernetes object. DNS providers and Kubernetes already coerce to lowercase to prevent that; the RFC argues the buildpack spec should enforce it for the same reason rather than letting the collision surface three layers downstream.

The 253-character red herring

One number to get right before it hardens into lore: 253 characters is not the proposed limit. That figure appears in the RFC as the ceiling of the alternative the authors considered and rejected — enforcing RFC 1123 host name syntax instead of DNS label syntax. Host names allow dots in valid positions and run to 253 characters total. The RFC rejects that option for two reasons worth quoting in spirit: buildpacks cannot know the fully qualified domain an image will deploy under, and a platform could never safely use the process type as a subdomain if a buildpack were allowed to emit a 254-character type.

That rejection is the whole design in miniature. A host-name-shaped type is routable but not composable — you cannot stick billing.cron.team-a under myapp.com and still reason about which label the platform owns. A label-shaped type is a building block: the platform can project it into a Service name, a subdomain, a preview URL, or a kubectl-addressable resource without parsing or re-chunking it. The strictness is the feature.

Why the spec, not every platform's slugifier

The RFC's motivation section gives two scenarios, and both are things platform teams hit constantly. First, routing: after building a web service image, a developer wants static-assets.myapp.com and api.myapp.com — but a Static_Assets process type cannot appear in a domain name, so somebody must transform it. Second, dynamic resource management: driving kubectl autoscale replicaset static-assets from the process types declared in the image — but Kubernetes rejects Static_Assets as an object name, so again somebody must transform it.

Today that somebody is each platform, privately. Every PaaS that turns process types into Kubernetes object names, Service DNS entries, or per-process preview URLs ends up inventing its own coercion: lowercase here, underscores-to-dashes there, truncation somewhere else, each with its own edge cases. That works until the tenant moves. An image whose web_worker type became web-worker on one platform and webworker on another carries two different identities for the same process, and the difference lives in nobody's manifest — it is emergent behavior of two slugifiers the tenant never saw. Debugging "it worked on platform A" when the process identity itself shifted under you is miserable precisely because nothing in the tenant's repo changed.

Pushing the constraint upstream into the buildpack contract fixes the boundary cleanly. The spec owns the alphabet: what a process type may contain. The platform owns enforcement and presentation: reject non-compliant types at build or intake time, with an error message naming the offending type and the rule. Buildpack authors targeting the new API version get one rule to satisfy instead of N platforms' undocumented coercions to reverse-engineer. Nobody needs to guess what web_worker becomes anymore, because web_worker stops existing.

The migration bill

This is a breaking change and the RFC says so plainly: it requires a new Buildpack API version. Old-API buildpacks keep their old alphabet; new-API buildpacks must comply. The cost concentrates in two places.

First, buildpacks that define process types dynamically — generated from filenames, entry points, discovered services — must implement coercion logic: substitution, filtering, truncation, or some combination, applied before the type is written. Static types just get renamed by their authors. Dynamic types need code, and truncation needs care: two long types that truncate to the same 62-character prefix collide, so naive type[:62] is a bug factory. Suffix hashing or head-plus-hash schemes are the standard answer, and every dynamic-type buildpack gets to implement one.

Second, tenants with already-shipped non-compliant types face a rename. Renaming a process type is not a label edit; the type can be embedded in DNS records, Kubernetes resource names, autoscaler targets, dashboards, and runbooks. The rename propagates everywhere the old string leaked, which is the same blast radius as renaming a Service — manageable, but real work with a cutover, not a find-and-replace.

To soften the landing, the RFC proposes a --strict-process-types flag for pack and the lifecycle, settable to warn or error, rolling through three phases: default warn (noisy but green), then default error (red until fixed), then optionally always-on error. One clarification worth making because summaries blur it: this flag is the RFC's own proposed mechanism, not the existing CNB_DEPRECATION_MODE variable. That variable — warn, error, or silent, defaulting to warn — already exists for a different job: governing behavior when a deprecated platform or buildpack API version is in use, per RFC 49 and RFC 110. The RFC 329 authors chose a dedicated enforcement flag over reusing the deprecation-mode variable, which makes sense once you see the difference: this is a content restriction inside a new API version, not a countdown on an old one.

Two questions remain explicitly unresolved in the RFC. Should other spec resources get the same treatment? Image names are the obvious candidate and the likely answer is no — URL-based image references legitimately contain periods and slashes. And is this change alone worth minting a new Buildpack API version, or should it ride along bundled with other features? That bundling decision is where the timeline risk lives: a breaking change waiting for traveling companions can sit approved-but-unmerged for a long time.

What a platform should do before the merge

You do not need the RFC merged to benefit from it. If you operate a PaaS that consumes buildpacks, adopt the label rule at intake today: validate process types against the DNS label shape when the image arrives, and when one fails, suggest the coerced name in the error instead of silently applying your own slugification. That single behavior — loud rejection with a suggested fix, rather than quiet coercion — converts the cross-platform identity hazard from the previous section into a five-minute rename at the source.

The deeper payoff is architectural. Once process types are guaranteed label-shaped, they become safe to project anywhere names are needed: per-process subdomains, preview URLs keyed by type, Kubernetes Jobs named after the process they run, autoscaler targets derived from the image manifest. Each of those is a feature that is annoying to build on stringly-typed input and trivial on a constrained alphabet. The RFC is eleven paragraphs of naming pedantry that unlocks a category of platform features — which is usually a good trade.

Watch the API-version bundling question for timing, and watch buildpack authors' coercion schemes for the truncation-collision bug class. Everything else is just renaming things, which — as the old joke goes — remains one of the two hard problems. At least after RFC 329, the names will be valid DNS labels.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex