App slow, crashing or cold-starting
Why apps slow down, crash or cold-start
Emergent apps run on KEDA-powered Kubernetes clusters with event-driven auto-scaling and scale-to-zero when idle. This architecture keeps costs low and resources efficient, but it means some performance characteristics differ from always-on hosting.
Scale-to-zero and cold starts
When your app receives no requests for several minutes, KEDA scales it down to zero running pods. The next request will experience a cold start - typically 5-15 seconds - while Kubernetes provisions a new pod, pulls the container image and boots your application.
Cold starts are normal and expected for low-traffic apps. High-traffic apps remain warm because continuous requests keep at least one pod running.
Keep critical apps warm
If cold starts impact user experience, consider upgrading to a tier with a higher minimum replica count or configuring a health-check ping from an external monitor (e.g. UptimeRobot) every 2-3 minutes.
Pod Disruption Budgets by tier
Emergent applies Pod Disruption Budgets (PDBs) to ensure availability during cluster maintenance, upgrades and node rotations. The guarantees vary by publishing tier:
| Tier | Minimum available pods | Impact |
|---|---|---|
| Starter | None | No PDB guarantee |
| Launch and above | 1+ | Zero-downtime rolling updates |
Warning
Apps on lower tiers may experience brief downtime during platform updates. For production workloads, use Launch tier or higher.
Auto-scaling behaviour (KEDA)
KEDA monitors HTTP request rate, CPU and memory metrics. When demand increases, KEDA automatically provisions additional pods. When demand drops, pods scale back down.
Scaling thresholds:
- Scale up when average CPU >70% or request queue depth >10
- Scale down after 5 minutes of low load
- Scale to zero after no requests for 10 minutes (configurable per tier)
Info
Auto-scaling reacts to sustained load, not instant spikes. A sudden traffic burst may briefly slow response times until new pods become ready (typically 10-20 seconds).
Common causes of slow performance
Memory or CPU limits exceeded
Each tier allocates specific resource limits per pod. If your app exceeds these, Kubernetes throttles CPU or kills the pod (OOMKilled) and restarts it.
Check your app's resource usage:
- Open the Workspace and navigate to your app
- Click Re-publish to open the Managing Publishing panel
- Click View Logs in the Overview tab
- Out of memory kills show up as restarts here
If you consistently hit limits, either optimise your code (see below) or upgrade to a tier with higher per-pod resources.
Profile your application
Use
console.time() (Node.js), cProfile (Python) or framework-specific profilers to find slow functions or database queries.Optimise or upgrade
Refactor expensive operations, add caching or move to a higher tier if the workload is legitimate.
Slow database queries
MongoDB is shared infrastructure; poorly optimised queries can slow your app and impact other users.
Common culprits:
- Missing indexes on frequently queried fields
- Large result sets fetched without pagination (
/limit
)skip - Unbounded regex queries or full collection scans
Tip
Use MongoDB's
.explain() method to inspect query plans. Add indexes for fields used in find(), sort() and aggregation pipelines. See Database (MongoDB) for indexing guidance.Blocking I/O or unoptimised dependencies
Heavy synchronous operations - file I/O, image processing, PDF generation - block the event loop in Node.js or the main thread in Python, stalling all other requests.
Solutions:
- Move heavy tasks to background workers or queues
- Use streaming APIs for large file uploads/downloads
- Lazy-load large dependencies only when needed
- Replace heavy libraries with lighter alternatives (e.g.
instead ofdayjs
)moment
Cold dependency fetches
If your app fetches external resources (fonts, CSS frameworks, third-party APIs) on every request, network latency compounds. Cache responses or bundle assets at build time.
Common causes of crashes
Unhandled promise rejections / exceptions
Emergent's runtime restarts crashed pods automatically, but repeated crashes trigger CrashLoopBackOff - the pod won't restart until you fix the code.
CrashLoopBackOff
If logs (Re-publish -> Overview -> View logs) show repeated crashes within seconds of startup, you have a boot-time error (missing env var, broken import, failed DB connection). Fix the root cause - the pod won't stabilise until the error is resolved.
Out-of-memory kills (OOMKilled)
When a pod exceeds its memory limit, Kubernetes terminates it immediately. You'll see
OOMKilled in the View Logs panel.
Common causes:
- In-memory caching of large datasets (use Redis or MongoDB instead)
- Memory leaks (closures holding references, event listeners not cleaned up)
- Large request payloads without streaming
Mitigation:
- Add memory profiling (Node.js:
, Python:--inspect
)memory_profiler - Implement response streaming for large data exports
- Use external storage (MongoDB, S3-compatible object storage) instead of in-memory buffers
- Upgrade tier if legitimate high-memory workload
Missing or incorrect environment variables
If your app expects an API key, database URL or config value that isn't set, it may crash on boot or on first use.
Note
Emergent automatically injects
MONGO_URL, DB_NAME, REACT_APP_BACKEND_URL, and CORS_ORIGINS. Custom env vars must be set via the app's Secrets tab (Preview → Manage → Secrets). The Universal LLM Key is injected only if enabled.Debugging workflow
Open your app in the Workspace, Click Re-publish to open the Managing Publishing panel, then click View Logs in the Overview tab. Logs persist for 7 days (30 days on Pro/Enterprise).
Check for missing environment variables, file-system paths (containers are read-only except
/tmp), or dependencies that rely on native binaries not present in the container image.Large container images take longer to pull. Minimise dependencies, use multi-stage Docker builds (if custom Dockerfile), and ensure your app framework starts quickly (avoid heavy initialisation logic at boot).
Quick performance checklist
Add database indexes
Use
.createIndex() for fields in find(), sort() and aggregation $match stages.Enable response caching
Cache expensive computations, API responses or rendered HTML for a few seconds or minutes using in-memory LRU cache or Redis.
Paginate large result sets
Never fetch entire collections. Use
limit() and skip() or cursor-based pagination.Wrap async code in try/catch
Prevent unhandled rejections from crashing the app. Log errors and return user-friendly messages.
Test under realistic load
Use
autocannon (Node.js) or locust (Python) to simulate concurrent users and identify bottlenecks before they hit production.Still experiencing issues?
If performance problems persist after applying these fixes, describe your symptoms in chat and agents will profile your app, suggest optimisations or recommend a tier upgrade if resource limits are the bottleneck.

