Production overview
Choose a deployment path, then configure health checks, security, migrations, and recovery.
Ridu deploys as one Go binary containing the API, operation engine, workers, generated schema, and static admin. PostgreSQL and MongoDB remain external durable dependencies; SQLite runs in the process against a local file. An optional object backend remains separate. Bun and Node are build-time tools, not production servers.
Start with Build and run to produce the binary and launch it on your own machine. Use Deploy to Railway for a complete hosted example. The sections below cover the settings and operating rules shared by production platforms.
Deployment checklist
- Pin one coordinated Ridu release across the CLI, Go module, npm packages, and official plugins.
- Run
ridu checkand application tests against committed generated contracts. - Create and review any schema migration; rehearse
ridu migrate verifywith the exact release in staging. MongoDB’s final cutover verification runs command-scoped after the production drain. - Restore a recent selected-database and object-storage backup into staging and rehearse the plan at representative scale.
- Build the production binary and immutable deployment image.
- Follow the abuse-prevention checklist for access rules, authentication limits, hosts, origins, proxies, request bounds, uploads, and edge rate limiting.
- For any release with a changed migration-history fingerprint—including a manifest change or a same-manifest data-only artifact—drain old PostgreSQL or MongoDB replicas and workers, or quiesce every same-host process using the SQLite file; follow the adapter’s exact migration and recovery-point order below.
- Start the new binary and wait for
/readyzbefore sending traffic. - Exercise login, access-filtered reads, writes, drafts, uploads, tasks, and representative admin flows.
- Keep monitoring and the complete prior recovery point through the rollback window.
Build one runtime artifact
ridu check
ridu migrate verify
ridu buildridu build resolves config, updates generated contracts, compiles the Svelte admin, embeds its
assets and the exact ordered migration filename/digest fingerprint, and writes the Go binary to
dist/. Build with the supported Go and Node.js versions listed in
Releases and compatibility, the package manager recorded in ridu.toml, and the
committed lockfiles. The runtime image needs the binary, certificates, and application environment;
it does not need source,
node_modules, Bun, or Node. A direct go build omits the fingerprint and therefore fails closed
in ordinary production startup with an official database adapter.
Run a PostgreSQL- or MongoDB-selected project with the exact migration history used during verification:
DATABASE_URL="$DATABASE_URL" \
RIDU_ADDRESS=':8080' \
./dist/contentA SQLite-selected project instead supplies an absolute local RIDU_SQLITE_PATH, keeps every
process using that file on the same host, and does not horizontally replicate the deployment.
Ridu never mutates the production schema at startup. The process performs readiness preflight
before binding the listener and fails if the store, storage, complete applied migration history,
executable manifest, or required maintenance contract cannot be verified. PostgreSQL, SQLite, and
MongoDB all compare the live ordered ledger with the fingerprint embedded by ridu build.
Configure the generated boundary
Generated projects read the deployment topology from environment variables:
DATABASE_URL- Supply a PostgreSQL URL using
sslmode=require,verify-ca, or preferablyverify-full; or a MongoDB URL selecting one database, SCRAM-SHA-256, a replica set, and verified TLS. Plaintext and TLS fallback are rejected. RIDU_SQLITE_PATH- Supply an absolute local SQLite path or
file:URI. Do not use a shared network filesystem or mount the file on multiple hosts. RIDU_ADDRESS- Set the complete listener address. It defaults to
:8080. PORT- Set only the port. Hosting platforms commonly provide this variable; Ridu uses it when
RIDU_ADDRESSis unset. RIDU_POSTGRES_UPLOAD_LOCK_CONNECTIONS- Size the separate pool for cross-process object locks. Its default is four; count these connections alongside document connections on every replica.
RIDU_ALLOWED_HOSTS- List public hosts, optionally with ports. Set it in production because an empty list accepts every syntactically valid host for development compatibility. Include the host your platform's health check sends, such as Railway's
healthcheck.railway.app; Ridu refuses unlisted hosts before routing, including/readyz, and logs the first refusal of each. RIDU_ALLOWED_ORIGINS- List exact browser origins allowed to make cross-origin requests. Same-origin admin traffic needs no entry.
RIDU_TRUSTED_PROXY_CIDRS- List immediate proxy networks allowed to supply the forwarded address and scheme. Leave it empty when the Go process is directly exposed.
RIDU_TRUSTED_PROXY_HOPS- Count the proxies each request passes through, the trusted one that connects to Ridu included, when the others can't be listed. Ridu then takes the client address from before them in
X-Forwarded-For, so sign-in limits apply per visitor. Set it only when every proxy rewrites or appends to that header. RIDU_STRICT_TRANSPORT_SECURITY- Set the HSTS header. Use it only when HTTPS is permanent for the complete declared scope.
RIDU_READINESS_TIMEOUT- Bound database, object-storage, and custom readiness checks.
RIDU_READINESS_DRAIN_DELAY- Control the delay between becoming unready and beginning request shutdown. Unset or zero uses two seconds; a negative duration removes this propagation delay without disabling graceful shutdown.
RIDU_SHUTDOWN_TIMEOUT- Bound HTTP shutdown and runtime-resource cleanup.
RIDU_WORKER_DRAIN_TIMEOUT- Bound cooperative task and maintenance-worker drain.
Disable the readiness-propagation delay on platforms that atomically remove the old deployment
from routing before sending its termination signal. For Kubernetes or a self-managed load
balancer, choose it from measured endpoint/routing propagation and include it in the platform
termination grace period; two seconds is a default, not a universal safe value. On Railway, where
traffic cutover precedes termination, set RIDU_READINESS_DRAIN_DELAY=-1s and use the platform’s
separate drain window for in-flight work.
RIDU_ALLOW_INSECURE_DATABASE is for a development database on another host; loopback and
Unix-socket databases never need it. RIDU_ALLOW_UNVERIFIABLE_READINESS
is for a custom adapter only after equivalent external checks and auth maintenance exist. Neither is
a remedy for a failing production dependency.
Secrets belong in the platform secret store, not generated files or image layers. Rotate database, storage, signing, and external-provider credentials through an application-owned procedure that keeps readiness and rollback viable.
For MongoDB, keep the running application on a database-scoped credential and use that or a selected
database operator for plan, status, and up. Use separately scoped, short-lived credentials for
verification and backups. Never make the application a cluster administrator to simplify
operations.
Harden HTTP and proxies
The generated environment covers common topology. HandlerOptions and ServerOptions expose finer
bounds:
ridu.WithHandlerOptions(ridu.HandlerOptions{
AdminAssets: adminassets.FS(),
SecureCookies: true,
AllowedHosts: []string{"cms.example.com"},
AllowedOrigins: []string{"https://app.example.com"},
AllowedRequestHeaders: []string{"X-Acme-Tenant"},
TrustedProxyCIDRs: []string{"10.0.0.0/8"},
MaxBodyBytes: 8 << 20, // 8 × 2²⁰ = 8,388,608 bytes (8 MiB)
RequestTimeout: 20 * time.Second,
ReadinessTimeout: 5 * time.Second,
ContentSecurityPolicy: "default-src 'self'; object-src 'none'",
Observe: observeRequest,
Audit: recordAuditEvent,
RequestError: reportTrustedError,
JobError: reportWorkerError,
})
ridu.WithServerOptions(ridu.ServerOptions{
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
ShutdownTimeout: 15 * time.Second,
WorkerDrainTimeout: 15 * time.Second,
ReadinessDrainDelay: 5 * time.Second,
})The framework supplies conservative defaults; customize them from measured traffic. A negative request timeout is an explicit streaming opt-out and must be paired with endpoint-owned bounds.
Ridu gzips GET responses of 1 KiB or more that are JSON, HTML, CSS, JavaScript, SVG or other
text when the client accepts gzip, and sends Vary: Accept-Encoding. It leaves responses under
/api/auth/, and every response to other methods, uncompressed: those are the responses that can
carry a session or preview token, and compressing a secret beside text an attacker chooses can
reveal it through the response’s length (BREACH). Custom and plugin endpoints are compressed by the
same rules, so return secrets from POST endpoints, as Ridu does. A proxy in front of Ridu doesn’t
need to compress again.
TLS termination must replace untrusted forwarding headers before traffic reaches a configured
trusted proxy network. Keep CIDRs to immediate controlled hops. Secure cookies are enabled by the
production executor unless RIDU_SECURE_COOKIES=false; never disable them behind a public HTTPS
origin. Configure CORS only for intentional browser clients and list custom request headers
explicitly.
The default admin CSP is conservative and preview-compatible. Override it narrowly; disabling CSP because a plugin needs broad execution hides a supply-chain problem rather than fixing one. Use Prevent abuse for the public-traffic checklist and Security for the full trust boundary and request-work ceilings.
Apply migrations as a deployment step
Use the exact target binary and committed artifacts for planning and rehearsal:
ridu migrate plan --json
ridu migrate verifyShadow replay checks migration history, not production data volume or lock timing. Rehearse against a recent restored backup, set migration timeouts below the deployment deadline, and stop old writers for every maintenance-classified artifact.
PostgreSQL and MongoDB permit rolling overlap only for binaries embedding both the same manifest
digest and the same migration-history fingerprint. For a MongoDB release with any new artifact—even
an additive or same-manifest data-only one—the production cutover order is exact:
drain old replicas and workers; run verify with its command-scoped operational URL; capture the
matched database/upload snapshot; run up with the selected database-scoped application or
operator URL; run status after up; then start the new binary with the application URL. SQLite
does not promise horizontally replicated rollout overlap; quiesce every same-host process using the
file for the migration cutover.
Rollback mechanics are a separate concern from deployment overlap. PostgreSQL and MongoDB have no generated down migration, while SQLite exposes only the explicit reversible steps stored in its artifacts. A down migration can revert state, but it does not make incompatible application versions safe to run together; Ridu uses forward repair or complete recovery-point restoration instead.
Deploy migrations covers the release order, maintenance work, interrupted runs, and recovery; Safety checks lists the approval flags and safety codes.
Liveness, readiness, and drain
/healthz- Reports that the process is alive. Use it for restart policy; it does not confirm a matching schema or reachable dependency.
/readyz- Admits load-balancer traffic when the process is not draining and its migration ledger, document store, upload backend, and custom checks pass. PostgreSQL also verifies its physical schema, and MongoDB verifies the exact Ridu-managed indexes.
ridu migrate status- Provides the operator's deployment and drift report. It checks that the immutable ledger, phase and step completion, and managed database schema match committed history. Readiness does not replace it.
On SIGTERM or interrupt, Ridu marks the instance draining so readiness fails, waits the configured
drain delay, performs bounded HTTP shutdown, cancels request contexts, drains cooperative workers,
and closes runtime resources within a separate bound. If a task ignores context past the worker bound, its
lease remains fenced and recovers after expiry.
Set the platform termination grace period longer than readiness delay + longest admitted request + worker drain + resource-close budgets. Remove traffic on readiness before the platform sends a hard kill.
Observe the application
Ridu keeps telemetry vendor-neutral:
RequestObservationreports request ID, method/path, status, response bytes, duration, the stable error code, and the specificErrorReasonbehind it, such asorigin_deniedbehindaccess_denied.AuditEventreports security-relevant actions with request ID, resolved client IP, action, collection/document target, and actor collection/document identity.RequestErrorreceives trusted internal causes and recovered-panic detail while the HTTP response remains redacted.JobErrorreceives worker and scheduled-operation infrastructure failures; handler retry state is also queryable through durable tasks.- custom
ReadinessChecksadd required application or plugin dependencies without coupling Ridu to a metrics vendor.
The diagnostic callback is trusted and must enforce its own redaction and access policy. Audit callbacks are structured observations, not a tamper-aware durable audit log.
At minimum, alert on readiness failures, login throttle/recovery anomalies, migration and physical drift, exhausted database pools, repeated task retries or terminal failures, upload reconciliation deltas, elevated conflicts, and sustained latency or error-rate changes. Track RSS and connection use per replica; the performance baseline is not capacity planning for your schema.
Back up and test restoration
Back up the selected database and object storage as one recovery point. Retain the binary, configuration, generated manifest, and immutable migration history needed to interpret it. Database metadata without objects is incomplete; objects without matching references can retain stale data.
Regularly restore into an empty isolated environment and verify:
- migration ledger,
ridu migrate status, and zero physical drift; - collection, global, version, task, and auth-state counts;
- login, session rotation, access-filtered reads, update conflicts, and private upload delivery;
- representative object hashes and image variants;
- task claiming, scheduled publishing, and cleanup/reconciliation; and
- measured recovery-point and recovery-time objectives.
For PostgreSQL, logical dumps complement rather than replace managed point-in-time recovery. For SQLite, use an online backup or stop all processes using the file and quiesce WAL writers before a filesystem copy; never accept a copy of only the live main file. Restore the selected database and object storage to the same named point before accepting the drill.
For MongoDB, drain writers, complete command-scoped verify, then create a
database-scoped compressed archive with mongodump --archive --gzip --dumpDbUsersAndRoles before
running up; pair it with the upload-storage backup from the same point.
Restore into an empty target with
mongorestore --archive --gzip --drop --restoreDbUsersAndRoles, restore uploads from the same named
point, and confirm that unrelated databases were untouched. This is a logical dump/restore
workflow, not point-in-time recovery.
Stage, cut over, and roll back
For a PostgreSQL or MongoDB code release with the same manifest and migration-history fingerprints, canary beside the previous binary and compare errors, p50/p95 latency, RSS, database connections, response checksums, tasks, private uploads, drafts, and conflicts. Shift traffic only after the replacement stays ready.
For a MongoDB release with any new migration artifact:
- Rehearse the exact sequence in staging, then drain every old process and worker in production.
- Run
DATABASE_URL="$MONGODB_OPERATIONAL_URL" ridu migrate verify. It replays every artifact into a private shadow database, so semantic work needs no--allow-maintenancethere. - After verification, capture the matched database/upload snapshot with a separately scoped backup identity.
- Run
DATABASE_URL="$MONGODB_MIGRATION_URL" ridu migrate up, where the selected URL is the database-scoped application or controlled operator identity. Append--allow-maintenanceonly when the pending or incomplete history suffix contains semantic work. - Run
DATABASE_URL="$MONGODB_MIGRATION_URL" ridu migrate statusand require exact complete history and managed indexes. - Start only the target release binary carrying the expected manifest and migration-history
fingerprints with
$MONGODB_APP_URL, wait for readiness, and run acceptance checks. - Prefer a forward corrective migration. Restore only when the entire database/object pair and compatible binary can return to the same point.
For PostgreSQL, use its documented drain, recovery-point, up, and post-apply status contract;
SQLite requires every same-host process to be quiesced. Retry an immutable artifact after fixing an
operational blocker rather than editing it.
Use expand–migrate–verify–contract across separate reviewed releases for large transformations, while keeping each migration-history boundary on the documented coordinated cutover. A dual-manifest admission contract would be an optional deployment enhancement for teams with a strict zero-downtime requirement, not a prerequisite for safe production use.
Review Releases and compatibility for supported platforms and upgrade policy, PostgreSQL for pools and transactions, SQLite for the embedded operating envelope, MongoDB for its bounded replica-set profile, and Object storage for backend and reconciliation requirements.