Deployment
Deployment answers a different question from adapters. The adapter docs explain which runtime contract DaloyJS plugs into. This page helps you choose where to run a DaloyJS REST API and shows the packaging and platform config that matter in production.
- 01ciBuildpnpm install --frozen-lockfile, pnpm build
- 02Packagedistroless image or dist/
- 03Releasesigned image + SBOM attestation
- 04RunNode adapter, graceful shutdown
Node platforms
These providers all run the Node adapter. What changes is the platform config, health checks, and rollout mechanics.
Containerized Node service with fly.toml, health checks, and scale-to-zero machines.
Blueprint-based Node web service with healthCheckPath and autoscaling.
Auto-detected Node app with optional railway.json or railway.toml for start, health, and migrations.
Procfile-based Node dyno on heroku-24 or heroku-26 with the heroku/nodejs buildpack.
Node web server on Autoscale or Reserved VM with Publishing commands, Secrets, and port checks.
Production checklist
- Set
NODE_ENV=productionso 5xxdetailis redacted. - Set a sane
bodyLimitBytesper route group (don't default to 1 MiB everywhere). - Set
requestTimeoutMsto less than your load balancer's idle timeout. - Mount
secureHeaders(),requestId(), andrateLimit()globally. - Wire your structured logger and propagate
request-idto downstream calls. - Run contract tests in CI, fail the build if the spec drifts.
- Use
pnpm install --frozen-lockfilein CI; neverpnpm install.
Docker (Node, distroless)
Signed images + SBOM attestation
create-daloy --with-ci ships a deploy.yml that, after every successful push to GHCR, resolves the immutable @sha256:<digest>, signs the image with Sigstore Cosign (keyless / OIDC, no long-lived signing key), generates an SPDX SBOM for the image, and uploads it as a Cosign attestation (--type spdxjson). The job grants id-token: write alongside packages: write; the top-level workflow keeps permissions: {}. This closes the Aikido container-security checklist items for "Use Signed Images" and "Generate an SBOM." Verify any pulled image with:
Graceful shutdown
The Node adapter installs SIGTERM/SIGINT handlers by default. DaloyJS stops accepting new requests (returning 503) and waits up to shutdownTimeoutMs for in-flight requests to drain.
Reverse proxy
If you sit behind nginx / Caddy / a load balancer / a PaaS edge (Railway, Render, Fly, Heroku), declare the proxy posture so DaloyJS resolves the real client IP and stops refusing forwarded requests:
- Set
behindProxy: { hops: N }onnew App({ ... }), whereNis the number of trusted proxy hops in front of the app (a single edge proxy is1; Cloudflare in front of one PaaS edge is2). In production an unconfigured posture makes DaloyJS return500on the first request carrying anX-Forwarded-*header, so a misconfigured chain cannot feed spoofable client IPs torateLimit(), request-id propagation, or audit logs. UsebehindProxy: "none"when the app faces the public internet directly. - Once the posture is declared,
rateLimit()keys on the resolved client IP automatically, no customkeyGeneratorrequired. - Make the LB's idle timeout greater than DaloyJS's
requestTimeoutMs. - Make DaloyJS's
keepAliveTimeoutgreater than the LB's, Node adapter does this for you.
Edge / serverless REST APIs
DaloyJS can run on Vercel Functions, Cloudflare Workers, Netlify Functions, AWS Lambda, Fastly Compute, and Deno Deploy because the core is Web-standard Request → Response. Use these targets when you want per-request billing or a managed edge/serverless runtime instead of a long-lived Node process.
For a standalone Vercel REST API, create a single api/index.ts function and export the web-standard fetch handler, plus a vercel.json rewrite so DaloyJS routes at the site root (without it the root domain 404s):
See Vercel, Cloudflare Workers, Netlify, AWS Lambda, and Fastly Compute for platform-specific entry files.