Performance Optimization
To make an app faster on Temps, find the slow layer first — Web Vitals and request latency are already collected — then tune that layer: cache responses, give the environment the right CPU and memory limits or more replicas, fix slow database queries, and put a CDN in front for distant visitors. Because Temps runs on your own server, every layer is yours to change.
Where Temps-specific levers sit:
| Lever | Where |
|---|---|
| Real-user Web Vitals (LCP, INP, CLS, TTFB) | The project's Speed Insights page, see Analytics |
| Request volume and p95/p99 latency | Project overview; alert rules under Metrics → Alerts, see Monitoring |
| CPU and memory limits, replicas | Project Settings → Environment settings, see Scaling |
| Reverse proxy and TLS | The built-in Pingora proxy terminates TLS and forwards to your containers; your app's Cache-Control headers pass through to browsers and CDNs |
Build optimization
Slow builds extend your deploy time. The fastest wins:
Use multi-stage Docker builds to avoid shipping dev dependencies and build tools in the final image; for Next.js, the standalone output plus a slim runtime stage cuts the image to a fraction of its size. See Custom Buildpacks for the pattern.
Structure Dockerfiles to maximize layer caching — copy dependency manifests before source files so npm install is only re-run when dependencies change.
Enable TypeScript transpileOnly in ts-node or esbuild for faster compilation when type-checking runs separately in CI.
Caching
HTTP response caching
Set Cache-Control headers on static assets and immutable routes:
// Next.js route handler
return new Response(data, {
headers: {
'Cache-Control': 'public, max-age=31536000, immutable',
},
});
For dynamic routes that can tolerate brief staleness:
Cache-Control: public, s-maxage=60, stale-while-revalidate=300
Redis caching
Cache expensive database queries or external API responses in Redis:
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
const user = await db.query('SELECT * FROM users WHERE id = $1', [id]);
await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 300);
return user;
Cache TTLs should reflect how stale the data can safely be, not an arbitrary large number.
CDN integration
Temps serves all traffic directly from your VPS. For global audiences, put a CDN in front:
Cloudflare (recommended) — point your domain's nameservers to Cloudflare, enable proxying (orange cloud), and Cloudflare caches static assets at edge locations worldwide. No config changes to your app needed.
Cache rules to set in Cloudflare:
- Static assets (
/_next/static/*,/images/*,/fonts/*) — Cache Everything, Edge TTL 1 year - HTML pages — Respect Existing Headers (lets your
Cache-Controlheaders govern) - API routes (
/api/*) — Bypass Cache
Resource tuning
CPU and memory
Under-resourced containers cause slowdowns that look like application bugs. Check the environment's Metrics view (or the project's Metrics page) and look for:
- CPU throttling —
container.cpu_utilization_percentsustained above 80% means you need more cores - OOM restarts — container killed and restarted means it needs more memory
Adjust under Project Settings → Environment settings: CPU is set in cores, memory in MB (0 means no memory cap). Temps applies these as container limits. If one container is busy even with enough resources, add replicas instead; see Scaling.
Connection pooling
Databases accept a limited number of concurrent connections. Without pooling, each server process opens its own connection and you hit the limit quickly.
For Node.js with PostgreSQL (a linked Temps Postgres service injects POSTGRES_URL):
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.POSTGRES_URL, max: 10 });
Remember that every replica opens its own pool: 3 replicas with max: 10 can hold 30 connections.
For Python, use SQLAlchemy's built-in pool or PgBouncer as a sidecar.
Database profiling
Slow queries are the most common source of backend latency. Enable query logging in PostgreSQL:
ALTER SYSTEM SET log_min_duration_statement = 100;
SELECT pg_reload_conf();
To rank queries by average time, use the pg_stat_statements extension. It must be listed in shared_preload_libraries on the server; then create it once per database and query it with the connection string Temps gives you (POSTGRES_URL):
psql "$POSTGRES_URL" -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"
psql "$POSTGRES_URL" \
-c "SELECT query, mean_exec_time, calls FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 20;"
The most common fixes:
- Add an index on columns used in
WHERE,JOIN ON, orORDER BY - Avoid
SELECT *— fetch only the columns you need - Use
EXPLAIN ANALYZEto understand the query plan
Monitoring performance
The project overview shows request volume and p95/p99 latency, and the project's Speed Insights page shows real-user Web Vitals. To be told about a regression before users notice, create a metric alert rule under the project's Metrics → Alerts tab, and add an uptime monitor for availability. See Monitoring for the alerting steps.
Once you have performance baselines in place, consider scaling horizontally for traffic spikes, or move caching and data to managed Redis and managed PostgreSQL.