modelence.config.json file that tells it how, and the steps to set up an existing Node.js project.
If you are a person, hand it to your agent:
What Modelence Cloud runs
-
A Docker image built from the repository on an official Node.js image, 18 or newer (default
node-22-slim, set withimage). Thebuildcommands run in order in the resource’s root; only npm is preinstalled. -
One web process, started with the resource’s
startcommands. The last one must keep running and listen on the port in thePORTenvironment variable — a hardcoded port never receives traffic. -
Optional static directories (the resource’s
static) served in front of that process by the platform. Requests that match no file fall back to the directory’sindex.html, so single-page apps route on the client. -
Optional managed PostgreSQL databases and Redis caches, declared in
resourcesnext to the service. A deploy creates each one in the environment’s region, and the app gets its connection details through the variablesenvties to it. They need a paid Modelence Cloud plan. -
Optional telemetry, a
resourcesentry of type"telemetry". It gives the environment a private OpenTelemetry endpoint for the app’s traces and metrics, kept for 30 days and shown on the environment’s Monitoring page. It needs a Modelence Cloud plan with monitoring (Pro and up). Logs need no setup: the app’s output is collected and shown on the Logs page. -
Environment variables injected at start:
MONGODB_URIandMONGO_URL— a MongoDB Atlas database provisioned for this environment (both hold the same connection string).SITE_URLandROOT_URL— the environment’s public URL (both hold the same value).PORT— the port the web process must listen on.- Any variables the user defines in the dashboard under Environment variables.
- The variables
envties to a managed resource, such asDATABASE_URLfrom a PostgreSQL database’surl, orOTEL_EXPORTER_OTLP_ENDPOINTfrom telemetry’sendpoint.
MODELENCE_, andPORT, are reserved and cannot be defined by the user.
modelence.config.json reference
modelence.config.json sits at the project root and is the whole contract between the project and
Modelence Cloud. Comments (//, /* */) and trailing commas are allowed. Apart from a resource’s
type, every key is optional; a missing key takes the default.
The file has two top-level sections: resources (what the app is made of) and env (the variables
it expects).
A service entry of
resources takes:
A managed resource entry of
resources takes:
A deploy creates every managed resource the file declares that the environment doesn’t have yet, and
the new version starts only once they are ready. Resources are matched by name, so the same file works
in every environment:
- A resource that already exists is kept as it is, with its data.
planis read only when the resource is created, so changing it later has no effect. - A resource removed from the file is not deleted. Delete it in the dashboard under Resources when it is no longer needed; a database or cache is billed until then.
- Renaming a resource creates a new, empty one. The old one stays until it is deleted.
env declares one variable:
A value set in the dashboard overrides a
value written here, so a literal in the file is the
committed default rather than a fixed constant.
A variable with "type": "resource" takes its value from one output of a managed resource in the same
file, and takes only these keys:
The
url output is a full connection string: postgresql://...?sslmode=require for PostgreSQL and
rediss://... (TLS) for Redis. The other outputs are its parts, for clients that read them separately,
such as PGHOST and PGPASSWORD. These variables reach the running app only, not the build commands.
They can’t be set or changed in the dashboard, where they are listed with the resource they come from.
Telemetry’s outputs are the values of the standard OpenTelemetry variables:
endpoint for OTEL_EXPORTER_OTLP_ENDPOINT and headers for OTEL_EXPORTER_OTLP_HEADERS, which
holds the environment’s credentials. The endpoint takes OTLP over HTTP and is private: only the
running app reaches it, not the build or a local machine.
Apps built on the Modelence framework need no file:
npx modelence@latest deploy recognizes the
modelence dependency and keeps building them locally, as it always has. To build one on Modelence Cloud
instead, describe it like any other Node.js app: "build": { "commands": ["npm ci", "npm run build"] }
and "start": { "commands": ["npm start"] }.build runs npm install; "build": { "commands": [] } means
there is no build step. A missing start means there is no process. An empty command string "" is
invalid and is rejected.
Examples
Express API only — no build step, the server serves everything:modelence.config.json
modelence.config.json
client/ is built into client/dist and served at
/; requests that match no file reach the API. VITE_API_URL is scoped to the build as well because
Vite inlines it while building:
modelence.config.json
DATABASE_URL and REDIS_URL:
modelence.config.json
OTEL_LOGS_EXPORTER turns off the SDK’s own log export. The project depends on
@opentelemetry/auto-instrumentations-node, which --require loads before the server:
modelence.config.json
Setting up a project
Follow these steps in order. Do not skip the verification step.-
Inspect
package.json. Readscripts,engines.node,typeand the dependencies. Note which framework builds the frontend (Vite, Next.js, Create React App, Angular, Astro…) and what runs the server (Express, Fastify, Koa, Hono, NestJS, a framework’s own server…). -
Pick the install command from the lockfile.
package-lock.jsonin sync withpackage.json→npm ci. If it is stale or missing, usenpm install.pnpm-lock.yaml→corepack enable && pnpm install --frozen-lockfile.yarn.lock→corepack enable && yarn install --frozen-lockfile(Yarn 1) orcorepack enable && yarn install --immutable(Yarn 2+).
corepack enablemakes pnpm and Yarn available at the versionpackageManagerinpackage.jsonnames. -
Find the build output. Vite writes
dist/, Create React App and Angular writebuild/ordist/<project>/browser, Astro writesdist/, Next.js needsnext buildand its own server (next start), not a static mount, unlessoutput: 'export'is set. Check the framework config for a changedoutDir. -
Check the start script.
vite,vite preview,react-scripts start,next dev,nodemon,ts-node-devandtsx watchare development servers. Do not use them. Replace them with a production start (node dist/server.jsafter a TypeScript build,next start,node server.js) or, for a pure frontend, a static mount and nostart. Do not add database migrations tostart.commands: every container runs them on every start, several at once during a rollout, and a slow one is killed by the health check partway through. Leave migrations out of the file and tell the user to run them against the environment’s database before deploying. A step may go first instart.commandsonly if it is fast and safe to run in parallel, such asprisma generate. -
Make the server read
PORT. The listen call must useprocess.env.PORT, for exampleapp.listen(Number(process.env.PORT) || 3000). Bind to all interfaces (the default), not to127.0.0.1only. If the frontend and the API are served together, the server must be the one process — the static mount handles the frontend files, the server handles everything else. -
Use the provided databases. If the project needs MongoDB, read the connection string from
process.env.MONGODB_URI(orMONGO_URL) instead of a hardcoded one. If it needs PostgreSQL or Redis that it doesn’t already have hosted somewhere, declare a managed resource for it in step 7 and tie the variable the code reads (DATABASE_URL,REDIS_URL) to itsurloutput. A database hosted elsewhere (Supabase, Neon, PlanetScale and so on) works as it is: keep the connection code and read its URL and keys from environment variables. Other databases, such as MySQL, are not provisioned. See Compatibility. -
Write
modelence.config.jsonat the repository root with the values found above, as a single service entry inresources("type": "service"). Pickimagefromengines.node(defaultnode-22-slim; useslimunless the project already builds on Alpine). Setrootwhen the app lives in a subdirectory. Add any managed resources from step 6 next to the service, with their variables inenv. Add atelemetryresource only when the user asks for traces or metrics, or the project already uses OpenTelemetry: a deploy fails on a plan without monitoring. TieOTEL_EXPORTER_OTLP_ENDPOINTandOTEL_EXPORTER_OTLP_HEADERSto its outputs instead of a hardcoded collector. Add"$schema": "https://cloud.modelence.com/schema/modelence.config.json?version=1". -
Verify locally. Run the build commands, then the start commands with
PORT=3000set, in order — exactly as written in the file — and confirm the app answers onhttp://localhost:3000(for a static site, confirm thedirexists after the build and holdsindex.html). Fix the file or the code until this works; a deploy runs the same commands. -
Commit
modelence.config.jsontogether with any code changes (thePORTchange, the production start script). -
Run
npx modelence@latest deployin the project directory yourself, with a timeout of at least 15 minutes. It opens the browser, where the user signs in, picks the organization, application and environment (or creates a new application or environment there) and enters the values of any required variables the environment does not have yet. Tell the user to confirm there, then wait for the command to finish: it uploads the project and follows the build until the app is live. If you know the target, pass--app <app> --env <env>to preselect it; the user still confirms it in the browser.
Runtime values and secrets
When the app needs a value that must not be committed — an API key, an OAuth secret, a third-party URL — declare it by name in the file’senv section, without a value and with "type": "secret"
for anything sensitive. The deploy asks the user for it in the browser; it can be changed later in
the Modelence Cloud dashboard under Environment variables. A value only the build needs (a
VITE_* or NEXT_PUBLIC_* variable that is inlined into the frontend) gets
"scopes": ["build", "runtime"]; never give a build scope to anything the browser must not see. Mark
a variable the app runs without "required": false. Read the values from process.env as usual.
Non-secret settings that belong with the code (a base path, a public API path) go in the file’s
env section instead, with a value, so they are versioned with the repository. Give anything a
bundler inlines "scopes": ["build", "runtime"] so it reaches the build as well as the running app.
When the project is not compatible
Report the project as not compatible with Modelence Cloud, and say why, when:- It is not a Node.js application — Java, Python, Go, PHP, Ruby, .NET and so on, even with a Node.js frontend in front. The image runs Node.js only.
- It requires a second long-running process: a separate worker or queue consumer, a scheduler process, or a database other than the provided ones (MySQL, Elasticsearch…) that would have to run alongside the app. One web process is run, and MongoDB, PostgreSQL and Redis are the only databases provisioned.
- It needs Docker Compose or a custom Dockerfile to run.
VITE_SUPABASE_URL, also needs to be available at build time, as described in
Runtime values and secrets.
Then suggest what the user can do instead: replace the database with one that is provided (MongoDB
through MONGODB_URI, or a managed PostgreSQL or Redis resource), or keep the incompatible parts on
another host and deploy only the Node.js web app.
Do not move a worker, queue consumer or scheduled job into the web process. The web process can run as
several replicas, and each one would run every job — emails sent twice, charges made twice. If the
user wants to do it anyway, tell them that each job needs a lock held in the database, so that only
one replica runs it at a time.
Meteor
Early support — the commands below work but Meteor is not yet a first-class runtime.
ROOT_URL and MONGO_URL, which Meteor reads at start, are already injected. Match
image to the Meteor release: Meteor 3 runs on Node 22.
modelence.config.json