My Apps Runtime Limits - EyevinnOSC/community GitHub Wiki
My Apps Runtime Limits
Every application deployed via My Apps runs inside a container with defined resource limits. This page documents the current limits so you can design your application accordingly.
Memory limits by runtime
Each runtime type has its own memory allocation. The limit is a hard cap enforced by the container runtime; if your process exceeds it, the OS kills and restarts it.
| Runtime | Language / stack | Memory limit | Memory request |
|---|---|---|---|
| web-runner | Node.js | 2 Gi | 512 Mi |
| python-runner | Python | 1 Gi | 512 Mi |
| wasm-runner | WebAssembly | 512 Mi | 256 Mi |
| golang-runner | Go | 2 Gi | 256 Mi |
| dotnet-runner | .NET / ASP.NET Core | 512 Mi | 256 Mi |
| php-runner | PHP | 512 Mi | 256 Mi |
The memory limit is the ceiling. The memory request is the amount of memory the scheduler reserves for your container on the underlying node. Your container can use anywhere between the request and the limit; it is only terminated if it exceeds the limit.
CPU
No CPU limit is enforced on any runtime. Your application can use as much CPU as the underlying node can provide, without being artificially throttled.
The wasm-runner and dotnet-runner each have a CPU request of 25 millicores, which is a scheduling hint rather than a cap. All other runtimes have no CPU request either.
In practice this means a CPU-bound workload can burst freely, but will share node resources fairly with other tenants during periods of high aggregate load.
Request body size
The maximum request body size enforced at the ingress layer varies by runtime. Requests larger than the limit receive an HTTP 413 response before reaching your process.
| Runtime | Max request body |
|---|---|
| web-runner | 64 MiB |
| python-runner | 64 MiB |
| wasm-runner | 1 MiB |
| golang-runner | 1 MiB |
The 1 MiB limit on wasm-runner and golang-runner reflects the nginx ingress default — these runtimes do not have a per-namespace override on the combined ingress. If your Go or WASM app needs to accept larger uploads, route the upload through a separate web-runner or python-runner service, or use a pre-signed URL pattern so the file goes directly to an OSC-hosted object store (such as Garage S3) rather than through your application.
For web-runner and python-runner, the 64 MiB ceiling covers most API payloads. If your application needs to handle very large file uploads, consider chunked uploads or the same pre-signed URL approach.
php-runner has no per-namespace nginx body size override either, so it falls back to the same 1 MiB nginx default — but live testing has shown requests up to 50 MiB reaching the app with no nginx-level rejection, so nginx is not the binding constraint for php-runner uploads. The binding constraint is PHP's own upload settings:
| PHP setting | Value |
|---|---|
upload_max_filesize |
2M |
post_max_size |
8M |
| Max file uploads per request | 20 |
These are PHP's built-in defaults on php-runner; there is no documented way to override them per app. If your app needs to accept larger file uploads, route the upload through a separate web-runner or python-runner service, or use the pre-signed URL pattern described above.
dotnet-runner is not yet listed in this table. Use the get-runtime-limits MCP tool (see below) to check the authoritative current value.
Request timeout
HTTP responses must be sent within 60 seconds. The ingress proxy closes the connection and returns HTTP 504 if your handler does not respond within that window.
If your application performs work that takes longer than 60 seconds (video processing, report generation, data imports), structure it as an async pattern:
- Accept the request and return a job ID immediately (HTTP 202 Accepted).
- Process the work in the background.
- Let the client poll a status endpoint, or deliver the result via a webhook.
This approach works well with My Jobs for background job processing.
Plan-based limits
Resource limits are determined by your application's runtime type, not by your subscription plan. A Node.js app on the Free plan gets the same 2 Gi memory limit as a Node.js app on the Business plan. There are no plan-specific resource tiers for My Apps.
Practical implications by runtime
Node.js (web-runner, 2 Gi): The 2 Gi limit is comfortable for most Express, Fastify, or Next.js applications. Watch for in-memory data accumulation in long-running server processes; streamed responses and pagination keep memory usage predictable.
Python (python-runner, 1 Gi): The 1 Gi limit is sufficient for typical Flask or FastAPI services. If you load large ML models or datasets into memory, measure your process's resident set size locally before deploying — many Python model-serving workloads exceed 1 Gi and are better suited to a dedicated instance from the OSC catalog.
WebAssembly (wasm-runner, 512 Mi): WASM apps tend to have small memory footprints. The 512 Mi limit is not usually a constraint for compiled modules, but note that it also applies to any JavaScript host runtime wrapping the module.
Go (golang-runner, 2 Gi): Go's garbage collector is efficient with heap memory. The 2 Gi limit gives substantial headroom; the lower memory request (256 Mi) means the scheduler places Go containers more aggressively on smaller nodes.
NET / ASP.NET Core (dotnet-runner, 512 Mi): The 512 Mi limit is tight for some dotnet workloads. Keep an eye on the default GC heap size, which the .NET runtime sizes relative to total available memory. If you hit OOM restarts, trim the heap ceiling with the DOTNET_GCHeapHardLimit environment variable via your Parameter Store.
PHP (php-runner, 512 Mi): PHP Runner serves your app with Apache and mod_php, which has more memory overhead per request than the plain single-process servers used by the other runtimes. The limit is 512 Mi (measured from the container cgroup), so keep Composer-based apps (Laravel, Symfony, Slim) lean. If setup.sh compiles PHP extensions, cap build parallelism with -j2: nproc reports the node's 8 CPUs, and an out-of-memory kill during the build leaves no log line.
Checking if your app is hitting limits
OOM kills appear in the application logs as an abrupt restart with no preceding error messages — the process simply stops mid-run. To investigate:
- Go to My Apps and check the status column for repeated restarts.
- Ask the AI agent: "Show me the logs for my app called myapp" — look for lines around the restart boundary.
- If logs end abruptly without an exception, an OOM kill is the likely cause.
Request timeouts are easier to spot: the client receives HTTP 504, and your application logs show the handler was still running when the connection dropped.
My Jobs (python-job-runner) limits
My Jobs run as ephemeral Kubernetes Jobs via the eyevinn-python-job-runner. Their limits differ from My Apps — there is no nginx layer, and the job pod is torn down after each run.
| Property | Value |
|---|---|
| Memory limit | 512 Mi |
| Memory request | 256 Mi |
| CPU request | 250 m |
| CPU limit | None |
| Max job duration | 6 hours (enforced by deploy-manager via activeDeadlineSeconds) |
| Concurrency | One run at a time per job — a second trigger is rejected while a run is in flight |
| Retry on failure | None — failed jobs must be re-triggered manually via the Run button or API |
| Cron timezone | UTC — all cron expressions use UTC |
| Disk storage | Ephemeral node-level storage only, no persistent volume — data does not survive between runs |
Jobs that need to persist output between runs should write to an external store (object storage, a database, or an OSC-hosted MinIO instance).
Checking limits via MCP
You can query live cluster values for any runtime directly from your AI assistant:
get-runtime-limits
runtime: "web-runner"
For My Jobs limits:
get-runtime-limits
runtime: "python-job-runner"
Returns the current memory limits, CPU constraints, and job-specific constraints (max duration, concurrency policy, timezone) for that runtime. Use this tool to get authoritative values instead of relying on documentation that may lag behind infrastructure changes.
Related resources
- Managing Custom Apps — Restart, rebuild, HA mode, and logs
- Deploy or Publish Your Application — How to deploy an app on OSC
- Parameter Store — Managing environment variables for your app
- My Jobs — Background job processing for long-running work
- Networking and Tenant Isolation — Platform-wide request and resource limits