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. -
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.
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).
Each entry of
resources takes:
Each entry of
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.
Apps built on the Modelence framework need no file:
modelence 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
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 injected database. If the project needs MongoDB, read the connection string from
process.env.MONGODB_URI(orMONGO_URL) instead of a hardcoded one. Other databases are not provided — see Compatibility. -
Write
modelence.config.jsonat the repository root with the values found above, as a single entry ofresourceswith"type": "service". Pickimagefromengines.node(defaultnode-22-slim; useslimunless the project already builds on Alpine). Setrootwhen the app lives in a subdirectory. 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). -
Tell the user to run
npx modelence deployin the project directory. On the first run it opens the browser to sign in and pick the application and environment; afterwards it remembers them.
Runtime values and secrets
When the app needs a value at runtime that must not be committed — an API key, an OAuth secret, a third-party URL — tell the user to add it in the Modelence Cloud dashboard under Environment variables for the target environment, marked as secret. Values only a build needs (aVITE_* or
NEXT_PUBLIC_* variable that is inlined into the frontend) must additionally be ticked available at
build time; leave that unticked for anything the browser must not see. Read runtime 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 MongoDB (PostgreSQL, MySQL, Redis…). One web process is run, and MongoDB is the only database provisioned.
- It needs Docker Compose or a custom Dockerfile to run.
MONGODB_URI), 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