Other hosts
What you'll learn
What any Node.js host needs to run Easy CMS, and notes for Railway, Render, Fly.io and Coolify.
Before this page: Choosing a host, Docker.
Most platforms either build your app from its package.json or run the image from Docker. Either way, check the list below. The notes per platform come from each platform's documentation; we haven't run every one of them ourselves.
What any host needs
- Node.js 22.12 or newer. Most platforms read
enginesinpackage.json:"engines": { "node": ">=22.12" }. Edge runtimes are not supported. - Build, then migrate, then start. Build with
next build,nuxi build, or nothing for the standalone server; before starting, runeasy-cms migrate; then start withnext start,node .output/server/index.mjsoreasy-cms serve. Many platforms have a pre-deploy or release command for the migration; otherwise start witheasy-cms migrate && …. - Somewhere to keep data. Either a persistent disk (volume) for SQLite and
uploads, or a hosted Postgres and file storage. With SQLite on a disk, run one instance. - A process that keeps running, so the server runs scheduled jobs every minute. If the platform stops idle apps, call
<api>/jobs/runwithCRON_SECRETfrom a cron instead. - The environment variables from Choosing a host, and trust the platform's proxy (
trustProxyor--trust-proxy).
Railway
- Railway builds from
package.jsonand picks the Node.js version fromengines(or.node-version). The start command is yourstartscript. - SQLite: add a volume to the service and mount it inside the app, for example
/app/data, then setDATABASE_URL=file:/app/data/cms.db. Mountuploadsthe same way, or use S3 storage. Volumes are mounted when the app runs, not while it builds, so migrate in the start command. - Postgres: add Railway's Postgres and reference its
DATABASE_URLin the app's variables. - Services keep running unless you turn on Serverless (app sleeping); leave it off, or scheduled jobs only run while someone visits.
Render
- Create a Web Service from the repository. Render picks the Node.js version from
NODE_VERSION,.node-version,.nvmrcorengines. - SQLite: persistent disks are for paid services only. Mount one at an absolute path, for example
/opt/render/project/src/data, and pointDATABASE_URLat a file in it. A service with a disk runs one instance and has a few seconds of downtime per deploy. - Postgres: create a Render Postgres and copy its Internal Database URL into
DATABASE_URL. - Free web services stop after 15 minutes without visitors, which also stops scheduled jobs. Use a paid instance, or a Render Cron Job that calls
<api>/jobs/runwithcurl.
Fly.io
Fly runs the image from Docker (
fly launchfinds theDockerfile).SQLite: create a volume (
fly volumes create data --size 1) and mount it infly.toml:toml[mounts] source = "data" destination = "/app/data"A volume belongs to one machine and isn't replicated: run one machine, and back up yourself.
Keep it running:
fly launchsetsauto_stop_machines = "stop", which stops idle machines and the scheduled jobs with them. Setauto_stop_machines = "off"(ormin_machines_running = 1) under[http_service].Postgres: Fly Managed Postgres sets
DATABASE_URLwhen attached (fly mpg attach).
Coolify
Coolify runs on your own server and deploys from Git.
- Choose the Dockerfile build pack with the image from Docker, or Nixpacks.
- SQLite and uploads: add a volume mount under Persistent Storage, for example
/app/dataand/app/uploads. - Postgres: create one from Coolify's databases and copy its internal URL into
DATABASE_URL. Coolify can back up its databases on a schedule. - Coolify's proxy issues HTTPS certificates for your domain, and the app keeps running, so scheduled jobs run by themselves.