Standard backend (OpenKBS Cloud)
Run your functions and Postgres on OpenKBS Cloud instead of AWS Lambda and Neon: same CLI and handler contract, with only the real differences documented here.
With "backend": "standard" in openkbs.json, a project's functions and Postgres run on OpenKBS Cloud instead of AWS Lambda/Neon. Everything else — sites, storage, CloudFront, mail, domains, MQTT, AI proxy — is identical to Elastic projects, and all CLI commands and the function handler contract work exactly the same. This page covers only the real differences.
No implicit AWS credentials
Code using the AWS SDK without explicit keys (e.g. new S3Client() relying on a Lambda execution role) fails — there is no IAM role on the Standard backend. For file storage, use the platform API instead:
const res = await fetch(`https://project.openkbs.com/projects/${process.env.OPENKBS_PROJECT_ID}/storage/upload-url`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.OPENKBS_API_KEY}` },
body: JSON.stringify({ key: 'media/uploads/photo.jpg', contentType: 'image/jpeg' }),
});
const { uploadUrl, publicUrl } = await res.json();Files still land on S3 and are served through CloudFront exactly as on Elastic.
Function URLs
openkbs fn deploy prints URLs like https://api--ab12cd34.node1.openkbs.com (instead of *.lambda-url.*.on.aws). Use them the same way — including as the frontend API_BASE. CloudFront /fn/* routing works identically.
Schedules
Full cron support — both AWS forms work:
rate(N minutes|hours|days)cron(M H DOM MON DOW *)— standard cron semantics,?treated as*. The trailing year field must be*.
Postgres
DATABASE_URLis injected as usual; the host is adb.<node>hostname with a valid TLS certificate —ssl: { rejectUnauthorized: true }works.openkbs postgres migrateis not applicable on Standard.
Point-in-time restore (self-service, 7-day window)
The database is continuously backed up (every transaction). To recover data — for example after rows were accidentally deleted:
openkbs postgres restore --point-in-time "2026-07-05T17:03:55Z" # moment BEFORE the accident (UTC)
openkbs postgres restore-status # poll until "available" (typically 1-5 min)restore-status returns a connection string to a restored copy (db_<id>_restore) — the live database is never touched. Connect to both and copy back exactly what's needed (same role/password, so one client, two connections):
import pg from 'pg';
const live = new pg.Client({ connectionString: process.env.DATABASE_URL, ssl: { rejectUnauthorized: true } });
const snap = new pg.Client({ connectionString: RESTORED_CONNECTION_STRING, ssl: { rejectUnauthorized: true } });
// e.g. re-insert rows that exist in the snapshot but not live:
// SELECT from snap → INSERT INTO live (preserve ids with OVERRIDING SYSTEM VALUE if needed)When done, release the copy:
openkbs postgres restore-cleanupOne restore runs at a time per node; a second request while one is in progress returns an error — retry after a few minutes.
Runtime behavior
Functions are long-running processes handling requests concurrently (no cold starts, wake from idle in under a second). Avoid mutable global state that assumes one-request-at-a-time; per-request state belongs inside the handler. Connection pools and caches in module scope are fine and encouraged.