Fastly platform guide
Fastly Compute Deployment: Packages, Versions, Backends, and Rollback
A Compute deployment is an edge release and an origin-connectivity change. Make both reproducible, observable, and reversible before they carry production traffic.
On this page
Fastly Compute runs application code as WebAssembly at the edge. That makes small request decisions, response composition, and carefully bounded origin calls fast to distribute, but it also means a bad release can affect traffic globally. Treat the Compute package, its Fastly service configuration, and its runtime data stores as separate deployment surfaces with separate controls.
A managed orchestration layer sits in front of many origins and providers at once, so it experiences every Fastly Compute deployment as one input among many that can move latency, error rate, or cache behavior for a customer. At that scale, an unreviewed activation or an untested rollback path is not a single team's incident — it changes what every downstream dashboard and alert is telling you. The discipline below (explicit contracts, staged activation, retained rollback, cross-provider observability) is what makes a Compute release something an orchestration layer can trust rather than a black box it has to route around.
Overview
Outcome
Build and stage a minimal Acme Shop catalog handler, verify its artifact and health response, then activate a reviewed service version with a rollback target.
This tutorial is for DevOps and platform engineers operating a Compute service with one or more HTTP origins. It uses the Fastly CLI and control plane terminology current at publication. Confirm account entitlements, product limits, and CLI behavior in the linked Fastly documentation before automating the commands.
Do not turn the edge into an unbounded application tier
Compute is a strong fit for deterministic request handling, cache policy, routing, authentication checks, and short origin fetches. Keep long transactions, authoritative writes, unbounded fan-out, and strict global consistency in systems designed to own that state.
Prerequisites
- A non-production Fastly Compute service and origin domain, plus the correct production service ID and an identifiable prior active version.
- A least-privileged CI token stored in the CI secret manager, a pinned Fastly CLI, and the project language toolchain.
- An approved backend contract covering TLS, host override, health, timeouts, and an origin owner who can validate traffic.
- Synthetic test data, retained edge and origin telemetry, release stop conditions, and a named rollback operator.
1. Establish the Fastly Compute deployment contract
Start with a narrow contract for each route. Record the accepted methods and body limits; cache key and cacheability; selected backend; outbound headers; timeouts; retry policy; expected response classes; and fallback behavior. Include a release identifier in a response header only for a restricted test domain or authenticated diagnostics route, not as a dependency for clients.
A useful way to think about a Fastly Compute deployment is as four coupled artifacts that must move together but can be verified independently: the WebAssembly package, the numbered service configuration, the backend contract, and the runtime data stores. Most production incidents trace back to one of these four changing without the others being reviewed at the same time.
Fastly services have numbered configuration versions. A version can be cloned, validated, activated, and locked; locked versions cannot be edited. Preserve the service ID, active version number, package digest, source revision, and configuration change together in the deployment record. This is the minimum evidence needed to distinguish a package regression from an origin, backend, or data-store change.
Define stop conditions before the rollout: elevated origin 5xx responses, backend timeouts, a changed authorization outcome, a cache-key regression, or a p95/p99 latency increase beyond the route budget. Name the operator who can activate the prior known-good version and the origin team that receives a direct-origin or connection surge alert.
Acme Shop scenario
Acme Shop exposes only GET /products/<slug> through a Compute service. The handler may call the reviewed static backend acme_catalog_origin; it returns 405 for other methods and 404 for other paths. It does not handle account, checkout, payment, or writes.
- Public handler
Only the documented anonymous catalogue route is admitted.
- Package artifact
The exact built package and checksum are recorded.
- Staging service
Synthetic success, negative, and failure paths run before activation.
- Catalog origin
Only reviewed public reads reach the named backend.
The JavaScript handler is built into a package, checked on staging, then activated as a versioned edge release against the catalog origin.
2. Prepare credentials and tooling
Install a pinned, reviewed Fastly CLI version in CI. To verify it and authenticate locally, run:
fastly version
fastly auth login
You can also authenticate with a CLI profile. In CI, provide FASTLY_API_TOKEN from the CI secret manager rather than committing it, printing it, or placing it in fastly.toml. The CLI documents command flags, environment variables, and local credential storage as authentication options; use a token limited to the service and operations the pipeline actually needs.
The CLI compiles a Compute package locally and uploads the resulting artifact. Install the language toolchain required by the selected starter or SDK, then pin its version in the repository or build image. Build from a clean lockfile-based dependency installation. A changing compiler, transitive dependency, or generated bundle means a package with the same source revision may not be the same release.
For a new project, scaffold a supported starter rather than inventing the manifest:
mkdir edge-router
cd edge-router
fastly compute init
The initializer writes fastly.toml. For JavaScript projects, install the declared dependencies before building. For an existing service, set service_id in the manifest or provide it through the CI environment so the pipeline cannot accidentally create a new production service.
WebAssembly edge packages: resource limits and sizing
Because Compute runs your WebAssembly edge packages inside a per-request sandbox rather than a long-lived process, the platform enforces hard resource ceilings that a traditional application deployment does not have to think about. Design the handler and its dependency tree against these limits, not against what happens to work in local testing:
| Resource | Documented limit | Why it matters for a deployment |
|---|---|---|
| Compiled package size | 100MB (lower on trial accounts) | A growing dependency tree or bundled asset can push a build over the ceiling; fail the build, not the deploy, when it does. |
| CPU time per request instance | 50ms of active processing | Time the sandbox spends computing, not waiting on I/O. A busy-loop, heavy regex, or synchronous JSON transform on a large body can exhaust this before any origin call. |
| Wall-clock runtime per request instance | 2 minutes (60s on trial accounts) | Bounds how long a handler may hold a request open, including while awaiting a slow backend. |
| Heap / stack memory | 128MB heap, 1MB stack per execution | Large in-memory buffers or deeply recursive code fail here first, independent of CPU time. |
| Backend requests per execution | 32 (10 on trial accounts) | Caps fan-out from a single request; a naive N+1 backend-call pattern hits this quickly. |
| Dynamic backends per service | 200 | Relevant if you resolve backends from a reviewed configuration value rather than using only static backends. |
Two consequences follow directly for deployment practice. First, a package that built and served correctly in development can still fail in production purely on request shape: a larger payload, a deeper object graph, or a slower upstream can cross a CPU-time or memory ceiling that a small synthetic test never approaches. Include at least one deliberately oversized or worst-case input in staging tests, and treat a Fastly Memory Exceeded (heap_exhausted) or vCPU Limit (cpu_timeout) error event the same as a code defect, not as infrastructure flakiness. Second, fastly compute validate checks a built package's structure before upload without deploying it — run it in CI immediately after fastly compute build as a fast, non-destructive gate:
fastly compute build
fastly compute validate --package pkg/edge-router.tar.gz
Validation catches a malformed manifest or packaging error early; it does not evaluate runtime CPU or memory behavior, which only staging traffic reveals.
3. Separate package, service configuration, and runtime data
fastly.toml describes how the CLI builds and packages the application and can configure the local server. It is not a place for a production API token or plaintext application secret. Its scripts.build command must produce bin/main.wasm; the CLI packages that artifact for upload.
Keep service configuration under review alongside the package: domains, TLS, static backends, health checks, host override, TLS verification, backend timeouts, connection limits, logging endpoints, and cache policy. The backend API exposes these as configuration fields. In particular, a Compute backend timeout is a connection failure, not a partially successful response, so set a route-level deadline in code that is shorter than the user-facing budget and handle the failure deliberately.
Use the correct edge data store for each runtime value:
- Config Store: small, non-sensitive, infrequently changed configuration such as a route map or feature default.
- KV Store: larger, non-sensitive data that needs more frequent updates, with eventual-consistency implications assessed.
- Secret Store: small sensitive values such as API tokens, signing keys, or passwords.
These stores are versionless: updates take effect without a service-version increment. That is useful operationally but means a data update can change production behavior independently of a package deployment. Give every store an owner, change record, validation rule, and rollback procedure. Do not use a shared cache, Config Store, request headers, or logs to carry a secret.
4. Make backend behavior explicit
A named backend is the origin contract. Specify the hostname or address, port, TLS requirements, certificate and SNI expectations, host override when needed, connect/first-byte/overall timeouts, health check, and concurrency limit. Verify that the origin accepts the resulting Host header and that its certificate matches the configured verification name. Do not disable certificate checks to get a deployment through.
Static backends are configured on the Fastly service. Dynamic backends can be useful for controlled tenant routing, but derive them only from an allowlisted configuration value, never directly from a user-supplied URL or Host header. Otherwise a Compute service can become an SSRF relay. Apply an explicit route-to-backend map and deny unknown routes.
For local development, define a harmless backend substitute and test store data. This belongs in a developer-only manifest or ignored test fixture, not in a production secret file:
manifest_version = 3
name = "edge-router"
language = "javascript"
service_id = "YOUR_NON_PRODUCTION_SERVICE_ID"
[local_server.backends.origin]
url = "http://127.0.0.1:8080"
override_host = "origin.test"
health = "healthy"
[local_server.secret_stores]
test_secrets = { file = "test/fixtures/secrets.json", format = "json" }
Use fake values in test/fixtures/secrets.json, add the file to .gitignore if it can contain developer credentials, and never point a local test at an unprotected production origin. Local Secret Store data is test data; local encryption does not model production Secret Store protection.
5. Test in layers before uploading
Build the exact package CI will deploy:
fastly compute build
The expected artifact is a package archive under pkg/. Record its checksum and build metadata with the source revision. Run language-level unit tests before this command, then exercise HTTP behavior with the Fastly local server:
fastly compute serve --watch
curl -i http://127.0.0.1:7676/health
curl -i -H 'Host: app.test' http://127.0.0.1:7676/products/42
Test success, invalid input, unauthenticated input, a cacheable public response, a private response, a backend 4xx, connection refusal, slow origin, and an unhealthy backend. Assert that secrets, authorization headers, cookies, and internal routing headers are absent from responses and logs.
The local server is valuable but not a production replica. Fastly documents that it has no readthrough HTTP cache, so cache-control behavior and cache hit ratio cannot be proven locally. It also does not transit Fastly routing infrastructure, lacks several connection details and headers, and uses configured or mocked backend health. Test those properties on an isolated Fastly service with a non-production domain before production activation.
Steps
Minimal JavaScript handler
This handler intentionally has one public, read-only route and a named backend. It rejects identity-bearing requests before the readthrough cache can look them up or populate them. The backend name is a reviewed service configuration value, not client input.
/// <reference types="@fastly/js-compute" />
addEventListener("fetch", (event) => event.respondWith(handle(event.request)));
async function handle(request) {
const url = new URL(request.url);
if (url.pathname === "/health") return new Response("ok\n", { status: 200 });
if (request.method !== "GET") return new Response("Method Not Allowed", { status: 405 });
if (!/^\/products\/[a-z0-9-]+$/.test(url.pathname)) return new Response("Not Found", { status: 404 });
if (request.headers.has("Authorization") || request.headers.has("Cookie")) {
return new Response("Public catalogue endpoint does not accept credentials", { status: 400 });
}
return fetch(request, { backend: "acme_catalog_origin" });
}
The final fetch() deliberately uses Compute's normal readthrough cache only
after the request has matched the anonymous public contract. If this route must
later accept credentials, do not remove that guard: send the credentialed
request with cacheOverride: new CacheOverride("pass") or use a separately
reviewed private route and cache design.
6. Deploy to an isolated service and observe it
Deploy first to a separate development or staging service with a non-production origin and domain:
fastly compute deploy --service-id "$FASTLY_SERVICE_ID" --status-check-path /health
The CLI supports a service ID, selected service version, deployment comment, and status-check path and timeout. Require a meaningful health response that checks the route's required dependencies without exposing data. A 200 from a process that cannot reach its required origin is not an adequate release gate.
A separate non-production service is not the only pre-production option, and it is worth distinguishing it from Fastly's native staging environment. Staging activates a candidate version of the same production service without serving live traffic: PUT /service/$service_id/version/$version/activate/staging (or the equivalent control-panel/CLI action) makes that version reachable through a dedicated Anycast IP or a DNS record you point at it, on the same edge network, TLS configuration, and region compliance as production. It is closer to production fidelity than either the local server or a fully separate service, but it has real limits worth planning around: it requires at least one prior production activation to exist, it cannot stage domain or TLS changes, it does not share cache state with production, and staging traffic is billed the same as production traffic. Use it as the last gate before activation, after a separate non-production service has already exercised the failure paths that would be unsafe to run against production-adjacent infrastructure.
Use the following command for short, controlled staging diagnosis:
fastly log-tail
It streams standard output, standard error, and runtime errors, but it is not a production audit log. Configure a real-time logging endpoint for retained production telemetry. Emit structured, bounded-cardinality fields such as:
- request or trace ID, route template, method, status, and response class
- package digest or release ID, service version, selected backend, and cache outcome
- backend latency, timeout or retry reason, and safe failure mode
- an opaque configuration revision or secret version identifier, never the secret value
Redact Authorization, Cookie, signed URLs, client tokens, request bodies, and sensitive query parameters at the source. Join Fastly events with origin access logs through a request ID or trace context so an alert can show whether the edge, backend connection, or origin application is responsible.
Correlate release telemetry across every provider you run
If Acme Shop also fronts other routes through a different CDN, WAF, or DNS provider, a Compute release cannot be judged in isolation from what those providers are reporting for the same window. A managed orchestration layer such as MYO ingests Fastly's real-time logs alongside origin and other-provider telemetry under one release identifier, so a p95 regression or authentication-failure spike is visible as one event, not as separate signals someone has to correlate by hand across dashboards.
$ fastly compute build
SUCCESS: Built Compute package (pkg/acme-catalog.tar.gz)
$ fastly compute deploy --service-id "$FASTLY_SERVICE_ID" --status-check-path /health
SUCCESS: Deployed package to the selected service version
- Positive: staging
GET /products/demo-mugreturns the expected public catalog body and the health endpoint returns200. - Negative:
POST /products/demo-mugreturns405,/accountreturns404, and a request carryingAuthorizationorCookiereturns400without reaching the catalog origin or the shared cache. - Failure: a deliberately unavailable staging backend produces the reviewed safe failure rather than a hung request. If the health check, route contract, or backend error budget fails, do not activate production.
7. Activate deliberately and retain rollback
For production, clone the active service version in the Fastly control panel or through your infrastructure workflow. Apply the intended backend and service changes to that clone, validate it, upload the reviewed package to the intended version, and activate only after the deployment gates pass. Lock the resulting good version so later edits require a new, reviewable version.
Validate before activation and preserve the prior locked version because a Compute service version changes all of its traffic at once.
Immediately after activation, run synthetic requests from representative regions and compare them with the baseline. Watch edge and origin 5xx rate, backend timeout rate, origin request volume, cache outcomes, p95/p99 latency, and authentication failures. Fastly notes that replacement packages may take up to about a minute to begin handling requests, so keep the observation window longer than propagation and normal cache warming.
Rollback
Rollback is an activation operation, not a rebuild under pressure:
- Keep the previous working package and locked service version identifiable in the release record.
- If a stop condition is met, activate that known-good version using the control plane or the approved API automation.
- Verify the active version, synthetic checks, origin volume, error rate, and logs after propagation.
- Preserve the failed package, version number, correlated logs, and metrics for investigation. Do not edit the failing version in place.
If the incident is caused by a versionless Config, KV, or Secret Store update, activating an earlier service version will not undo it. Use the store-specific rollback procedure, then verify both the data revision and the serving package.
Checklist
Versionless-store caveat
Config Store, KV Store, and Secret Store changes take effect independently of a service version. Record the old value or approved replacement before changing a store; package rollback alone cannot restore it.
Why Compute rollback is fast but activation is all-or-nothing
The reason Compute rollback is a near-instant activation rather than a rebuild is the same reason it offers no built-in partial rollout: a service version is a single atomic unit. Activating a version — forward or back — replaces 100% of the traffic a service handles; Fastly does not currently offer a native, percentage-based canary or weighted split between two Compute service versions. Plan your release gates around that constraint rather than assuming a gradual, self-healing rollout exists underneath you.
Where a genuinely gradual rollout is a hard requirement, teams commonly build it themselves with a small routing layer: one Fastly service holds the customer-facing domain and forwards to two backend services (the current and candidate release) through a director or explicit weighted backend selection, shifting a configurable share of requests to the candidate and watching the same stop conditions used for a full activation. This adds real operational cost — an extra service to secure and observe, and cache behavior that needs a force-pass or otherwise deliberate design at the routing layer — so reserve it for routes where a bad release is expensive enough to justify it, and default to staged, fully-gated activation everywhere else.
This is also why the release contract from step 1 matters more here than in a platform with native progressive delivery: since Compute rollback cannot partially undo a bad release, the only real mitigation is catching the regression before full activation, through the isolated-service and staging-environment gates above, and having the previous locked version ready to reactivate the moment a stop condition fires.
Edge suitability and operational caveats
Choose Compute only where its execution model supports the outcome. Request handlers run in isolated WebAssembly sandboxes by default, and Fastly enforces product resource limits. Keep handler work bounded, set small outbound timeouts, cap retries, and make errors safe. A retry loop from many POPs can amplify a degraded origin.
Do not assume an edge location is an authority for user identity, inventory, balances, payment state, or write ordering. Read state from a designed system of record, expose staleness where it matters, and send long-running work to durable asynchronous processing. Cache only public or correctly isolated data; never cache a response with account data into a shared key.
Also protect the origin independently. Restrict origin ingress to Fastly or private connectivity where possible, authenticate edge-to-origin traffic, and overwrite untrusted forwarding headers at the trust boundary. Compute routing improves delivery control; it is not a substitute for origin network controls, application authorization, or a tested incident response plan.
Troubleshooting
| Symptom | Likely cause | Check | Safe correction |
|---|---|---|---|
| Build does not produce a package | Manifest or language toolchain is incomplete | Run fastly compute build from a clean checkout | Fix the build input; do not deploy a locally altered artifact. |
Staging health is 200 but catalog fails | Health route does not exercise the required route/backend | Test /products/demo-mug and inspect safe logs | Improve the staging gate before production activation. |
| Old behavior remains after version rollback | A versionless store changed behavior | Compare store change record with the package/version | Restore the approved store value, then validate both layers. |
| Origin sees unexpected hosts or timeouts | Backend TLS, host override, or timeout contract changed | Compare backend configuration with the known-good version | Reactivate the known-good version and correct the backend clone. |
| Requests fail intermittently under real traffic but never in local tests | Handler exceeds the CPU-time, memory, or backend-request-count ceiling on larger or slower real requests | Correlate failures with Memory Exceeded (heap_exhausted) or vCPU Limit (cpu_timeout) error events in retained logs | Reduce per-request work, cap payload/fan-out size, or move heavy processing off the hot path; re-test with worst-case inputs before reactivating. |
| Staging environment activation is rejected | No production version has ever been activated on the service, or the change touches domains/TLS | Confirm at least one prior production activation exists and that the change set excludes domain/TLS edits | Activate a first production version, or move the domain/TLS change to a reviewed production activation instead. |
| A partial rollout or percentage-based canary is expected but traffic is 100% on the new version immediately | Compute service-version activation is atomic; there is no native weighted split between two versions | Confirm whether a director/weighted-backend routing layer was actually built for this service | Use the isolated-service and staging gates before activation instead of relying on gradual production exposure. |
Release checklist
Before every production activation, confirm the source revision and package checksum are recorded; the service ID and target version are intentional; backends validate TLS and have tested timeouts; runtime stores contain validated data; local and staging tests cover failure paths; retained logs can correlate edge and origin activity; the previous version is known; and the rollback owner and thresholds are on call.
Fastly primary references
- Getting started with Compute
- Fastly CLI reference
- Compute CLI commands
fastly.tomlpackage manifest reference- Testing and debugging on the Compute platform
- Fastly service version API reference
- Backend API reference
- About edge data stores
- Compute resource limits
fastly compute validatereference- Working with staging
Related guides
Related reading
- Compute CLI commands
- Testing and debugging on the Compute platform
- About edge data stores
- Working with service versions
Make every Fastly Compute deployment observable and reversible
Optimi's Suite curates release, backend, and rollback controls across Fastly and your other edge providers, and gives you MYO visibility into Performance and Security signals for every activation — no unnecessary delivery complexity.
Discuss Fastly delivery operations