tako docs guide reference api
GitHub

footprint.md — the Profile and Footprint fields

Two layers of compute intent. Profile names it — the framework owns the values. Footprint pins exact values field-by-field over the profile — the escape hatch for when a workload's needs are known and specific. Prefer the profile; pin only what the profile can't know.

Declare

A Profile field on tako.ServiceConfig or tako.Compute selects its service's scaling profile (one service, one profile). The knobs (ProfileKnobs, spec §4.6):

profile CPU Memory Concurrency Min Max Timeout CPU between requests Startup boost
tako.ProfileDefault 1 512Mi 80 0 20 300s throttled no
tako.ProfileLatency 2 1Gi 40 1 100 300s always allocated yes
tako.ProfileBatch 2 2Gi 10 0 10 900s throttled no
(singleton) 1 512Mi 1 1 1 3600s always allocated no

Default scales to zero; Latency keeps an instance warm — min=1 bills continuously; Batch favors throughput per instance for background work.

Jobs default to batch and singletons to the singleton profile, but both are now DEFAULTS you can override with Profile — the provisioner used to overwrite whatever you asked for, which made the option accepted and ignored.

Latency's concurrency is deliberately LOWER than Default's, which reads backwards until you see what it buys: fewer requests packed onto one instance means less queueing behind a busy one, which is the whole point of the profile. Warmth (min 1), CPU always allocated, and a startup boost are the other three quarters — an instance that is up but throttled still stalls on the first request, and a warm floor of 1 does nothing for the second instance a traffic spike starts.

Profile and Footprint are declared on tako.Service for a package's app service, and on the handler itself for a worker, subscriber, job or singleton (where the one handler IS the service). They are refused on API and Page (tako/service-option) — a service has many routes and none is an honest place for a service-wide claim.

A Footprint field (S1) overrides the profile field-by-field:

type Footprint struct {
	CPU          int    // vCPUs per instance: 1, 2, 4, or 8
	Memory       string // per-instance limit, like "512Mi" or "4Gi"
	Concurrency  int    // concurrent requests per instance (1–1000)
	MinInstances int    // always-warm instances — they bill continuously
	MaxInstances int    // scale-out cap (1–1000)
	CPUAlwaysOn  Toggle // Inherit | On | Off — CPU between requests
	StartupBoost Toggle // Inherit | On | Off — extra CPU until serving
}

Traffic is its sibling, for how requests REACH the service rather than how much machine runs them:

type Traffic struct {
	Timeout         time.Duration // one unit of work: a request, or a job task
	SessionAffinity Toggle        // Inherit | On | Off — sticky routing
	HTTP2           Toggle        // Inherit | On | Off — HTTP/2 to the instance
}

The two are separate because they answer separate questions, and you reach for them at different moments: a footprint is sized against a workload's appetite, a traffic setting against how clients talk to it. Both layer over the profile the same way, and both are validated at analysis time.

Traffic.Timeout caps how long ONE unit of work may run — a request for a service, a task for a job. Cloud Run's ceilings differ, and so do the refusals: 60m for a service (past that you want a job), 24h for a job task.

CPUAlwaysOn is a three-state Toggle, not a bool, because a struct whose zero value means "inherit" cannot otherwise express "explicitly off". tako.ToggleOn forces allocation on a Default-profile service; tako.ToggleOff forces it off on Latency.

StartupBoost gives a starting instance extra CPU until it is serving. It is the one knob that moves cold-start latency, which is why Latency takes it and the others don't — you pay for it only during startup.

Traffic.SessionAffinity asks Cloud Run to send a returning client back to the instance it reached last. No profile turns it on, because it trades away even load spreading: sticky clients pile onto instances the autoscaler would rather have balanced. Reach for it when per-client warm state makes a real difference — a decoded model, a primed cache — and never for state you cannot lose. It is best-effort, and an instance can disappear between any two requests.

Refused on jobs and singletons: an execution serves nobody, and a singleton is one instance answering its own health check.

HTTP2

Traffic.HTTP2 keeps HTTP/2 all the way to your instance instead of letting Cloud Run downgrade it at the edge.

Read that carefully, because the thing it does not do is the thing people expect. Browsers already reach Cloud Run over HTTP/2 with TLS, whether you set this or not. Cloud Run terminates TLS at its frontend — always, for every service; a container can never terminate TLS itself — and by default it then forwards HTTP/1.1 over the last hop. HTTP2 changes only that last hop, to HTTP/2 cleartext (h2c). The hop is still encrypted by Google's infrastructure; "cleartext" describes the application protocol, not the wire.

So this is not a TLS setting and not a knob for browser performance. It is worth turning on for request multiplexing and streaming between the frontend and your handler — long-lived streams, many concurrent small requests, gRPC-shaped traffic. For ordinary request/response JSON the last hop gains close to nothing, which is why it is off by default, matching Cloud Run's own default.

A tako server always speaks HTTP/2, deployed with this on or not: Serve wraps every handler in h2c, which upgrades an HTTP/2 preface and passes HTTP/1.1 straight through. That matters because Cloud Run stops downgrading the moment the setting is on — a container that could not answer h2c would fail every request rather than falling back. You can confirm it locally against tako dev, the same check Google's docs prescribe:

curl -i --http2-prior-knowledge http://localhost:8080/

Refused on jobs and singletons, for the same reason affinity is.

Shutdown is not a knob and deliberately so — see observability.md. Cloud Run's 10s window is fixed, and tako divides it by watching rather than by budgeting: the drain ends when the last request does, and whatever is left goes to the flush. There is no number a service could usefully set.

Zero fields inherit from the profile; set fields override it. The min=0 nuance: a zero field means inherit, so an explicit always-scale-to-zero floor is expressed by choosing a min=0 base profile (Default or Batch), not by MinInstances: 0. On job handlers only CPU and Memory apply — an execution has no concurrency, no scaling, no idle CPU and no cold start to boost. Their one traffic setting is Timeout. Singletons take StartupBoost too, but not the shape that defines the primitive.

Use

The layering, from examples/gallery/app.go:

// Batch gives the resizer throughput knobs; the footprint pins what
// the profile can't know: image decodes are memory-hungry per
// request, so more headroom, fewer per instance, capped fan-out.
MakeThumb = tako.Worker(thumbs, makeThumb,
	tako.Compute{
		Profile:   tako.ProfileBatch,
		Footprint: tako.Footprint{Memory: "4Gi", Concurrency: 4, MaxInstances: 5},
	})

CPU and min-instances are NOT pinned — they inherit from batch. That layering is the whole design.

Local

No effect on tako dev or tako test — everything runs in one process. The claims are still fully validated at analysis time, and tako gen graph shows them side by side: service gallery-makethumb (package gallery, profile batch + footprint (memory 4Gi, concurrency 4, max 5)).

Cloud

Footprint fields must be constants — they are read at analysis time, and validated twice: each field against Cloud Run's limits, then the RESOLVED combination (profile base plus overrides) against Cloud Run's cross-field rules, so a footprint that only pins CPU cannot silently pair it with too little inherited memory. Impossible combinations fail the build, not the deploy.

tako deploy prints what the footprint resolves to, so the plan carries the price, not just the override: compute: 2 cpu / 4Gi / concurrency 4 / instances 0–5 (profile batch + footprint memory 4Gi, concurrency 4, max 5) (jobs get a task compute: line). Standing money is priced, never absorbed — raising min-instances above the profile's warns:

⚠ footprint on <service> keeps 2 instance(s) always on — they bill continuously whether traffic comes or not

(Gallery's footprint never triggers this — it inherits batch's min 0. The warning fires exactly when YOUR MinInstances exceeds the profile's.)

Test it

The claims live in golden tests — go test ./pkg/analyzer -run TestErrors/footprint — and the full walk (declaration → summary → live gcloud values → pricing warning) is validate/footprint.md.

Errors you'll meet

Not yet