Limits
Every figure on this page is enforced by the platform. Where a limit depends on your plan, the plan column says so.
Builds
| Limit | Value | What happens |
|---|---|---|
| Duration | 20 minutes | The build is stopped and the deployment fails with "the build ran out of time (20 minutes)". A build that runs this long is stuck — a working storefront build finishes in single-digit minutes. |
| Retries | one, when the build machine is lost | A build that fails is not retried: fix the cause and deploy again. A build whose machine is lost starts once more on a new one, within the hour a build may take, and its log shows the second attempt only. |
| CPU | 7 vCPU, dedicated | The build has a node to itself. |
| Memory | 12 GiB | Node exits with a heap error. The build runs with --max-old-space-size=8192. |
| Project bundle | 100 MB, uncompressed | The deployment is refused before anything is uploaded, and its error states the bundle's size. Contact Laioutr support. |
| Concurrent builds | 1 per platform | A second build waits until the first finishes. It is not queued on a node, so it costs nothing while it waits. |
| Log size | 5 MB | The rest of the log is cut, with a marker at the end. |
| Log retention | 7 days after the build ends | The deployment API answers with an empty log after that. Logs are archived when the build finishes; the build's own pod is removed an hour after it ends. |
| Dependencies | installed fresh | Every build downloads every dependency. The bundle carries no lockfile, so a build can resolve newer versions than the last one. |
| Bundle retention | 7 days | The uploaded project is deleted. Only the build reads it, so nothing afterwards depends on it — a rollback re-promotes a built image and never rebuilds. |
A build runs on its own node, which is destroyed when the build ends. It reaches the internet the way
a build does anywhere else — your own registry, a Git dependency, a postinstall script that fetches
a prebuilt binary all work. It cannot reach anything inside the platform: no other tenant, no node, no
metadata service.
Your environment variables are readable by everything that runs during the build, including the install scripts of every package you depend on. That is true of every build platform. Give a build only the credentials it needs.
Running storefronts
A size class fixes the shape of one pod. On M and L, how many pods you get follows from the page views your plan covers, so a larger plan on the same class means more pods, not bigger ones. S is one size whatever its page views: two pods, and a third under load.
On M and L, up to a few million page views a month the minimum of three pods already covers the traffic. Above it the count grows with your allowance.
| S | M | L | |
|---|---|---|---|
| CPU reserved | 0.5 vCPU | 1 vCPU | 1 vCPU |
| CPU ceiling | 2 vCPU | 2 vCPU | 2 vCPU |
| Memory reserved | 768 MiB | 768 MiB | 1536 MiB |
| Memory ceiling | 2 GiB | 2 GiB | 3 GiB |
| JavaScript heap | 1,536 MiB | 1,536 MiB | 2,304 MiB |
| Writable disk | 1 GiB | 1 GiB | 2 GiB |
| Adds pods under load | one, up to 3 | yes | yes |
| Preview deployments awake at once | 5 | 5 | 10 |
S adds one pod under load, never more. A spike beyond three pods makes an S storefront slower and never more expensive. M and L add pods up to a ceiling derived from your page-view allowance.
An S storefront runs at least 2 pods, M and L at least 3, and the platform spreads them across separate machines wherever the cluster has machines to spread them over.
The JavaScript heap is 75 % of the memory ceiling; the rest is for Node itself. A storefront that
outgrows it stops with JavaScript heap out of memory and restarts.
Environments
Every environment other than production runs as a storefront of its own, on the class Laioutr chooses for it. It has the pod of S, adds no pods under load, and keeps one preview awake at once. While nobody visits it runs no pod at all: after 5 minutes without a request it sleeps, and the next request waits for it to start — about 4 seconds. An environment whose pod cannot start answers like a preview beyond the awake cap: it waits about 60 seconds and then gets 504. Its hostname is never indexed. The class cannot be changed, and environments are part of your license rather than billed by page view.
Per request
| Limit | Value | What happens |
|---|---|---|
| Request timeout | 125 seconds | The edge answers 524. Anything that takes longer belongs in a background job. |
| Request body | 100 MB | The edge answers 413 before the request reaches your storefront. Send large uploads from the browser straight to a storage service of your own. |
| Streams and WebSockets | 300 seconds | The connection closes. Reconnect from the client. |
| Startup | 120 seconds | A pod that has not answered its health check by then is restarted. At build time the limit is 60 seconds, and the deployment fails. |
| Rate limit | per hostname, about 370 requests a minute for each pod your plan can run at most, rounded up to the next hundred | 800 a minute for an S storefront on two pods, 1,200 for a storefront of at most three, 400 for an environment, and 400 for each deployment's own hostname. Requests over it get 429. It sits at about twice what those pods can serve, so ordinary traffic never reaches it. Each hostname counts on its own: the platform hostname, each of your domains and each deployment's hostname has its own limit, so a test run against a preview never uses up production's. On a production hostname, requests for your build's own files under /_nuxt/ count against a second limit of the same size. Every request that reaches your storefront on one hostname counts, whoever sends it, so one client sending too fast — a scraper, a load test — uses it up for every visitor of that hostname. Responses the edge serves from its cache do not count. Two gateways each allow half, so a burst that lands mostly on one of them meets the limit slightly early. |
| Programs that pose as a browser | challenged | A program that Cloudflare recognises as one and that sends a browser's user agent gets 403 and a challenge page it cannot solve. A program passes when its user agent does not start with Mozilla or Opera, or when it contains bot, crawler, spider or a +http contact link; search engines and the other bots Cloudflare verifies pass as well. A browser's user agent with your tool's name added to it still gets the challenge. So if your SEO crawler, monitor or feed checker gets the challenge, set it to send its own user agent. On /api/orchestr/ and /api/frontend/, which only your storefront's own pages call, every program but a verified bot gets the challenge: read data from a program through /api/public/ with an API key. Webhooks under /webhooks/ and requests to /api/laioutr/ and /api/public/ are never challenged. |
| Attack requests | refused | Cloudflare's managed WAF rules answer a request they read as an attack with 403. So do requests for a path with a segment that starts with a dot, such as /.env or /.git/, other than /.well-known/. If a legitimate request of yours is refused, send us the Ray ID from the error page. |
| Page loads from one client | 60 in 10 seconds from one address, for clients that are not a bot Cloudflare verifies | The edge answers 429 to that address's page loads for 5 minutes; its API calls and files still load. Only page loads count: a request for a file (any path with a dot), for a path under /_, /api/, /webhooks/ or /cdn-cgi/, or from a verified bot does not. It sits far above what a visitor, or several visitors sharing one address, send, so it stops a scraper or a scanner from slowing your storefront for everyone. A site crawl or a load test from one machine meets it: load at most 5 pages a second from one address. The 429 says so in its body. |
| Shutdown grace | 60 seconds | A request in flight while a pod is replaced gets this long to finish; a longer one is cut. |
Outgoing requests
Your storefront can reach any public address. Pods on one machine share its outgoing IP address, so an upstream API that limits requests per IP counts your storefront together with others on the same machine. A fixed outgoing address is not available yet.
Configuration
There are no runtime environment variables. Your project's configuration is built into the image, so changing a value means a new deployment.
Storage and state
Write only to /tmp, and only as a scratch space. The writable disk in the table above is the
ceiling; a pod that exceeds it is evicted. Nothing written by a storefront survives a restart, and
pods are replaced routinely. The platform provides no file storage, so keep files in a service of your
own.
The shared cache is a cache. It is one instance shared by every storefront on the platform, so an entry can be evicted at any time to make room for new ones, yours or another storefront's, and how long an entry stays depends on their traffic as well as yours. The cache starts empty after a restart. Never keep state there that you cannot rebuild.
The shared cache has two size limits.
| Limit | Value | What happens |
|---|---|---|
| One entry | 8 MB | The cache refuses the write and closes the connection, which the storefront reopens. That entry is never cached, so every request for it is a miss. Cache smaller pieces. |
| Replies waiting on one connection | 64 MB at most, or 32 MB for 60 seconds | The cache closes the connection, which the storefront reopens. Reads in flight on it fail. Read fewer or smaller entries at once. |
A storefront reaches the shared cache at LAIOUTR_CACHE_URL, and may use only the keys and channels
under LAIOUTR_CACHE_PREFIX:
| Command | Allowed | What happens |
|---|---|---|
| Keys outside your prefix | no | The command answers NOPERM. That holds inside a Lua script too. |
EVAL, EVALSHA, EVAL_RO, EVALSHA_RO, SCRIPT LOAD, SCRIPT EXISTS | yes | A script can use only your own keys and channels. |
FUNCTION, FCALL, and every other SCRIPT subcommand | no | The command answers NOPERM. |
PUBLISH, SUBSCRIBE and PSUBSCRIBE on channels under your prefix | yes | A message is delivered at most once. A restart of the cache drops every subscription and every message sent while it is down, so after a reconnect, subscribe again and assume that you missed messages. PSUBSCRIBE accepts only the pattern <prefix>:* itself: a narrower pattern answers NOPERM. |
| Channels outside your prefix, and keyspace notifications | no | The command answers NOPERM. |
A storefront cannot create a load balancer, open a node port, or claim a disk. Those are billable resources, and the platform creates none of them on your behalf.
Hostnames
Every storefront gets a permanent hostname of the platform's own from the day it is created,
<name>.laioutr.app, and that hostname serves production whether or not you have connected a domain.
Laioutr assigns the name when the storefront is created and keeps it for good, also when the project is
renamed. Once one of your own domains is canonical, the platform hostname answers
X-Robots-Tag: noindex, so the two never compete for the same content in search results.
Preview deployments
Every deployment has a hostname of its own, <name>-<9 characters>.laioutr.app, for as long as it is
kept. A deployment that is no longer kept answers with a not-found page.
| Kept | For how long |
|---|---|
| Production deployments you replaced | the last 2, for 24 hours — the window for a rollback without a rebuild |
| Previews | the newest deployment of each preview name, until a newer one of that name replaces it |
| All previews together | at most 10; the oldest goes first |
Rotating your project's content preview token withdraws the production deployments you replaced at once: none of them can be made live again without a new build.
A preview that nobody visits scales to zero after 5 minutes and wakes on the next request, so the first request to a cold preview is slower. Beyond the number of previews awake at once in the table above, a request to another preview waits about 60 seconds and then gets 504, until one of the awake previews sleeps. A preview whose pod cannot start answers the same way. Preview hostnames are never indexed.
Password protection
| Limit | Value | What happens |
|---|---|---|
| Passwords | one per project | A new password replaces the old one and logs out everyone who used the old one. |
| Bypass secrets | 20 per project, the Laioutr Studio secret included | Creating another is refused. Revoke one you no longer use. |
| Login lifetime | 7 days | The visitor enters the password again. |
| Password attempts | 10 per minute per IP address and hostname | 429, and the page says to wait until the minute ends. The count is kept per Cloudflare location. |
| Time for a change to take effect | up to 60 seconds | A removed password or a revoked bypass secret still works until then. |
| Apex hostnames | cannot be protected | The apex redirects to www; protect that hostname. |
| Previews | protected by default | Switch preview protection off for the environment, or send a bypass secret. |
A protected hostname that cannot be checked — for example while the platform's protection store is unreachable —
answers 503 with Retry-After. It never falls back to serving the page unprotected.
A delivery path under /webhooks/ is never checked, so it answers as usual even then;
What stays reachable says which paths count and why.
Password protection explains how to set it up.
Metrics
Your storefront's own metrics — the HTTP, Node.js runtime and Laioutr metrics it sends over OpenTelemetry — and a copy of its traffic and container metrics are kept per environment: production and each other environment of your project count separately. The platform keeps its own record for billing separately; nothing on this page changes what you are billed.
The copy of your traffic and container metrics counts against the same series, sample and label limits as
your own metrics. The platform also adds labels to every series your storefront sends — tenant,
deployment, service, job, instance, and the instrumentation scope's otel_scope_name and
otel_scope_version — and the metric name counts as a label too, so at least 22 of the 30 labels are left
for your own attributes.
| Limit | Value | What happens |
|---|---|---|
| Metric names | http.server.*, http.client.*, nodejs.*, v8js.*, laioutr.*, orchestr.* | Any other metric your code sends is dropped before it is stored. Rename a metric you need into one of these prefixes, or stop sending it. |
| Active series | 150,000 | New series are refused; series that already exist keep updating. Each pod writes its own series, and a replaced pod's series count for a few hours. A label with unbounded values — a URL, an id, anything a visitor sends — is the usual cause: remove it, or replace it with a small set of values such as the route. |
| Samples | 10,000 per second, bursts to 200,000 | Samples over the rate are dropped until the rate drops, including a backlog sent after an outage of ours. Samples more than 6 hours older than the newest your environment has sent are refused whatever the rate. Export less often, or send fewer series. |
| Labels per series | 30, including the metric name and the platform's labels | The series is refused. Drop attributes you do not query. |
| Label value length | 512 characters | The series is refused. Shorten the value; a value this long usually should not be a label at all. |
| Retention, standard | 5 days | Older data is deleted. Deletion works on whole days of data, so data is kept at least 5 days and up to about 2 more. For longer, contact Laioutr support about extended observability. |
| Retention, extended observability | 30 days | As above. Laioutr support moves an environment between tiers. Moving to extended keeps what is still there; days already deleted do not come back. Moving back to standard deletes everything older than 5 days within about a day. |
| After the environment is removed | up to about 32 days | The metrics are deleted. |
Logs
Your storefront's own logs — what its pods and builds write — are kept per environment, and so is a copy of the access log for your hostnames. The visitor's IP address is not in that copy.
| Limit | Value | What happens |
|---|---|---|
| Log volume | 2 MB per second, bursts to 4 MB | Lines over the rate are dropped until it falls. Other environments and projects are not affected. Log less: leave debug output off in production. |
| One output stream | 1 MB per second, bursts to 4 MB | Lines over the rate are dropped. A container's standard output and standard error are limited separately, so a single noisy container is limited before the whole environment is. |
| Log streams | 1,000 | New streams are refused. A stream is the standard output or the standard error of one container in one pod, and a restarted container starts new ones. The platform sets a stream's labels, so the count follows your pods: fix a container that restarts again and again. |
| Line length | 64 KB | Longer lines are cut, not dropped. Split a large payload over several lines. |
| One log call | 16 KiB of its message | The rest is cut, and the log view marks the entry as cut. Logs says what one entry holds. |
| Retention, standard | 5 days | Older logs are deleted. Deletion works on whole days, so logs are kept at least 5 days and up to about 1 more. For longer, contact Laioutr support about extended observability. |
| Retention, extended observability | 30 days | As above. Moving between tiers works as for metrics. |
| After the environment is removed | up to about 31 days | The logs are deleted. |
Traces
Your storefront's traces — the spans it sends over OpenTelemetry — are shown per environment: every span is filed under the environment that sent it, and each environment sees only its own. Not every request is traced: one in four is traced at all, and of those every failed request and one in five of the others are kept. A request that arrives with a trace context of its own follows that context's choice instead of the one in four.
| Limit | Value | What happens |
|---|---|---|
| Trace data | 30 MB per second, bursts to 30 MB, shared by every environment on the platform | Spans over the rate are dropped, for every environment, until it falls. Trace less detail in production. |
| Traces open at once | 10,000, shared by every environment on the platform | New traces are refused while 10,000 are still receiving spans. |
| Trace size | 5 MB | Spans beyond it are dropped; the trace keeps what arrived first. A request that carries another site's trace context joins that trace, and the 5 MB counts both. |
| Retention | 14 days, for every tier | Older traces are deleted. Extended observability does not change how long traces are kept. |
| After the environment is removed | up to about 15 days | The traces are deleted. |
Reading your traces in Cockpit
Each environment hosted on Laioutr Cloud has a Slow requests page, and a page for each trace.
| Limit | Value | What happens |
|---|---|---|
| Ranges | 15 minutes, 1 hour, 6 hours, 24 hours, 5 days, 7 days, for every tier | A stretch you select on a chart can reach back 14 days. Older traces are not shown. |
| Slow requests | the 50 slowest of the 500 most recent traced requests in the range | A busy range shows the slowest of its most recent requests. Choose a shorter range to see all of it. |
| Spans shown | 5,000, or 5 MiB of the answer | A larger trace shows its earliest spans and says it was cut. |
| Time per read | 15 seconds | The page says the read took too long. Try again, or choose a shorter range. |
| Freshness | up to 30 seconds; a trace up to 60 seconds | A list or a trace read again within that time is the same answer. |
| Reads at once | shared across the platform | A page can answer "busy" for a moment. Try again. |
What counts as a page view
Your plan's allowance counts page views: pages a visitor's browser renders.
- Every full page load, once the page has loaded — the first page of a visit, a reload, a new tab — and every navigation inside the storefront to a different path.
- Not a change of only the query or the hash — a filter, a sort, a tab — and not an error page or a page the browser restores from its back-forward cache.
- Not a client whose user agent names a known bot, and not a client that runs no page JavaScript, as most scrapers do not.
Your storefront reports each page view with a small request of its own, POST /api/frontend/cloud/view, so a page
served from the edge cache counts too. A visitor whose blocker stops that request is not counted, which only ever
lowers your count.
For a day on which the platform received no such request at all, such as from a storefront whose build does not send it, the day's page views are the HTML pages the platform served to clients that are not known bots.
Suspension
A suspended storefront keeps its hostnames and its images, answers every request with a holding page, and runs no pods. Nothing is deleted.
Laioutr Cloud
Laioutr Cloud is the built-in hosting and delivery environment for frontends created with Laioutr. It provides a ready-to-use infrastructure layer, so you can launch and operate your storefront without having to set up your own hosting, deployment pipelines, or runtime configuration.
Fair use
What Laioutr Cloud expects of a storefront beyond its enforced limits — monthly allowances per size class, what must never be served, and what happens when a storefront goes beyond them.