Designing Apps for d2o
Build each app so that replacing or adding an instance does not lose data or interrupt work. These principles apply to every supported framework, whether you deploy a web app, a worker, or a static frontend.
Keep durable state outside instances
An instance’s filesystem and memory belong to that instance. Files can disappear when it restarts or is replaced, and another replica cannot see them. Store structured records in a database and uploads, generated exports, and other durable files in object storage. A local SQLite file, upload directory, or in-memory session store is unsuitable as the only copy of production data.
Temporary files are fine for bounded scratch work: unpacking an upload, transforming an image, or preparing an export. Clean them up, limit their size, and copy any result you need to keep into durable storage before reporting success. A singleton app still has an ephemeral filesystem.
Keep sessions and shared coordination outside the web process, or use an appropriate signed-cookie session design. Treat Redis caches as rebuildable. The platform does not back up Redis: a queue surviving a worker restart is different from surviving loss of Redis itself. For essential jobs, retain enough information in a backed-up database to recreate or reconcile them. See the durability comparison.
Read configuration at runtime
Read service addresses, feature flags, and credentials from environment variables. Use secret references for credentials and attachments for managed services. Keep each Environment’s settings separate and commit neither credentials nor local .env files.
Validate required settings at startup. A missing secret reference can leave its variable absent; it does not necessarily prevent the process from starting. Exit with a useful message naming the missing setting, without printing its value.
Build-time settings and runtime settings serve different purposes. Version selection and frontend compilation happen during the build; database connections and application secrets are used by the running process. Do not require a live production database to compile an image. See configuration and build/runtime network access.
Anything embedded in browser JavaScript, HTML, source maps, or other static assets is public, even if the build tool calls its input an environment variable. Keep private API keys and database credentials in a server-side app. A runtime secret cannot make an already compiled frontend asset private.
Listen on the configured port
Web apps must listen on 0.0.0.0 and the injected PORT, rather than a fixed port or loopback-only address. Workers have no public URL; a worker may expose an HTTP health endpoint on its configured port or use a health-check command.
Give web apps a fast, unauthenticated health endpoint that reports successful startup. Avoid restarting every instance because a shared dependency briefly fails: use request timeouts and bounded retries for dependency failures. See health checks and their defaults.
Shut down gracefully and make retries safe
When replacing an instance, d2o sends SIGTERM and allows 30 seconds before force-stopping it. Allow a short drain period while routing catches up, then stop accepting new work, finish in-flight requests or jobs, and exit. Configure your framework’s shutdown handling and keep work within that window.
A worker can stop after making a change but before acknowledging its job. Design jobs to be safe to retry: use stable job IDs, unique database constraints, and repeatable writes. Acknowledge only after the result has been saved. Long jobs should checkpoint or be split into smaller pieces. Do not assume a singleton or a queue provides exactly-once execution.
Share connections and handle dependency failures
Use bounded connection pools per instance. Scaling from two instances to ten also multiplies their potential database connections; allow capacity for workers and release commands too. Set connection and request timeouts, reconnect after failures, and retry transient errors with backoff. Do not blindly retry non-repeatable operations such as charging a customer.
Send logs to stdout and stderr
Print application events and useful errors to standard output or standard error; d2o collects them across instances. Do not depend on log files inside the container. Include request or job identifiers when useful, but redact passwords, tokens, connection strings, and sensitive request data. See Logs.
Prepare for production
Keep process selection, port, health checks, and release commands in versioned app settings. Use backward-compatible database migrations because old and new code overlap during deployment, and rolling back code does not roll back data.
Continue with Take an Example to Production or the production checklist.