The server will not start

Not a bug. The API always selects FirecrackerProvider, has no local runtime fallback, and fails startup on non-Linux hosts. Run the control plane on a Linux/KVM host and use the CLI from your Mac.
createControlPlane() refuses insecure development defaults. All of TINY_TOKEN_SECRET, TINY_SECRET_KEY, TINY_SESSION_SECRET, and TINY_BROKER_URL are required, plus one complete ingress mode.TINY_MODE and TINY_ALLOW_DEV_LOGIN do not exist.
TINY_FIRECRACKER_KERNEL and TINY_FIRECRACKER_ROOTFS must point at files that exist. See Guest images.
The host needs KVM exposed, and the worker agent needs permission to use it. Nested virtualization must be enabled by your provider.

The plan refuses before uploading anything

This is the platform reporting honestly rather than failing halfway through a deploy. Check what the target actually has:

The deployment failed

The candidate microVM booted but /_tiny/health did not answer. Traffic never moved — the previous deployment is still serving.Usually the entrypoint. With build.output: dist, an entrypoint of index.js means dist/index.js. Check tiny logs.
A previously applied migration was edited. That is a deployment error by design — add a new migration instead of changing an applied one.
The backend fails the deployment rather than running the VM unfiltered. Firecracker itself does not filter traffic, so this is a security failure, not a degraded mode. Check nft and ip are present and the agent has permission.
A pause, not a failure. The plan stays valid and approving resumes the same deployment. See Approvals.

The app is slow

Almost certainly memory. Below roughly 256 MiB the Node runtime’s text pages do not fit in the guest page cache and the guest re-reads them from the virtio disk every scheduling slice.The runtime raises any serving VM to a 512 MiB floor for this reason — if you are seeing it, check what the guest actually got.
Nested KVM plus the serial console, which costs roughly 0.08 ms per byte.TINY_FIRECRACKER_SERIAL_TELEMETRY=false removes it — but tiny logs and usage metering read the serial log, so that deployment then reports neither. See Observability.
Expected. The app slept after lifecycle.sleepAfter and the gateway wakes it before proxying. Raise sleepAfter if the latency matters more than the idle cost.

Database problems

One open write transaction per environment at a time — that is what SQLite is. The data service refuses and says so rather than deadlocking. Use Postgres if you need concurrent writers.
Transactions are leased. One left open past the timeout is rolled back and its session reclaimed, so a crashed guest cannot hold a write lock forever.The usual cause is awaiting a slow remote call — a capability invocation, for instance — inside a transaction you needed to commit.
A query that would return more than the row limit is refused rather than streamed or truncated. Add a LIMIT and paginate.
date arrives as a calendar-day string like '2026-08-22'. new Date('2026-08-22') is UTC midnight and renders as the previous day anywhere west of Greenwich. Compare and store it as text.

Storage problems

That key is absent. A missing namespace or a superseded deployment raises instead — which is what lets you tell an empty namespace from an unreachable one.
Two different limits. The single-host nginx sets client_max_body_size 20m for browser uploads; the app’s own writes over the private link are bounded at 32 MiB. Raise the nginx one if needed.
Expected. etag is derived from size and modification time, not a content digest. Compare it with an earlier ETag for the same key, never across keys.

Access and routing

Apps live below /apps/<environment-route>/. An absolute /styles.css resolves above the prefix. Use relative URLs. The gateway strips the prefix, supplies X-Forwarded-Prefix, and rewrites root-relative Location redirects — but it cannot rewrite your HTML.
Check whether the manifest is visibility: public with requireLogin: false. Only that combination serves anonymous requests, and it deliberately gives null rather than a synthetic user.
By design. Visibility is read from the production revision, and a preview is unreviewed work at a guessable hostname. You cannot test the anonymous path on a preview.
A cross-organization ID returns 404, not 403 — confirming existence would be a disclosure. Check the token’s organization.

Getting more detail

Every response carries X-Request-Id, and the same value appears in error.requestId. Use it to correlate across the gateway, the control plane, and the deployment log.