# Elements vs Heroku Heroku is a cloud hosting platform, owned by Salesforce, built around `git push`: you push your code, and Heroku builds it and runs it. The build uses buildpacks, which detect the app's language and install its dependencies ([slug compiler](https://devcenter.heroku.com/articles/slug-compiler)). The app runs on dynos, containers you scale by type and count, and a release phase can run a command, such as a migration, before each release goes live ([release phase](https://devcenter.heroku.com/articles/release-phase)). Heroku Postgres and other services attach as add-ons ([add-ons](https://devcenter.heroku.com/articles/add-ons)), and pipelines, review apps and Heroku CI carry a change from a pull request to production ([pipelines](https://devcenter.heroku.com/articles/pipelines)). Heroku bills dynos and add-ons by wall-clock time, prorated to the second up to each plan's monthly price ([pricing](https://www.heroku.com/pricing), [usage and billing](https://devcenter.heroku.com/articles/usage-and-billing)). Elements is not a hosting company. It is an integrated app environment for people and their agents: a project server that runs while you work, a default app framework whose jobs and cron replace worker dynos and Scheduler, a bundled Postgres database, and a test runner wired into the deploy. Like Heroku, it deploys with one command, `elements deploy`, but the command reaches over SSH to an Ubuntu server you rent from whoever you like. Elements is billed as a monthly price for each machine, and none of it runs on a meter ([pricing](/pricing)). Elements keeps the one-command deploy and runs on one server what Heroku rents as separate parts. On Heroku, the web dyno, each worker, the database and every add-on are rented through Heroku, each on its own line of the bill, and each deploy builds a new slug and restarts your dynos. With Elements, the app, its database, jobs and cron run together on one server you rent from any provider, and a deploy sends only the files that changed into the running server. ## At a Glance | | Elements | Heroku | |---|---|---| | What it is | A one-command deploy, plus the framework and tests behind it, aimed at your own server | A hosting platform | | What you pay for | Tooling, per machine, per month ([pricing](/pricing)) | Each dyno and each add-on, by the month ([pricing](https://www.heroku.com/pricing)) | | Where your app runs | A rented server from the provider and city you pick | Dynos on Heroku's infrastructure | | A deploy | Changed files only, usually under a second, into the running server | A new slug, allowed up to 25 minutes to build, then new dynos booted from it | | Tests and migrations | One deploy step: tests against a migrated test database, then the real migration | Migrations in the release phase; tests in Heroku CI, billed by dyno time | | Background jobs and cron | Part of the app, on the same server, with the queue in Postgres | Worker dynos, a queue add-on, and the Scheduler add-on | | Database | Bundled Postgres, or any Postgres host | Heroku Postgres, an add-on | | Moving away | Change a server address and deploy | Replace each dyno, add-on, pipeline and review app on another platform | ## Every Part of a Heroku App Is a Separate Charge Heroku computes dyno usage from wall-clock time, and "any dynos scaled above 0 accrue usage, regardless of traffic or activity" ([usage and billing](https://devcenter.heroku.com/articles/usage-and-billing)). The web dyno, each worker dyno, Heroku Postgres, a Redis add-on for the job queue and every other add-on are separate charges. Scheduled tasks run on one-off dynos that count toward your usage ([Scheduler](https://devcenter.heroku.com/articles/scheduler)), and test runs in Heroku CI are billed by the dyno time they take ([Heroku CI](https://devcenter.heroku.com/articles/heroku-ci)). On an Elements bill there is one line per machine, and you know its amount before the month starts ([pricing](/pricing), [licenses](/learn/man/licenses)). Another job worker, cron schedule or custom service adds nothing to it, because nothing is metered. The app, its job worker, cron and Postgres, which Heroku bills as separate dynos and add-ons, run on one rented server with a load balancer that issues and renews TLS certificates, at the price its provider lists ([what deploy does](/learn/man/deploy/what-happens)). The Postgres on that server suits a prototype or a one-server app. For an app on several servers, or to hand backups and scaling to a provider, connect a hosted Postgres like Amazon RDS or DigitalOcean Managed PostgreSQL through `DB_HOST` ([setup](/learn/man/deploy/setup)). ## A Deploy Sends Changed Files, Not a New Slug Every `git push` to Heroku runs the build again: Heroku checks out your code, installs its dependencies, and packages a new slug, and a build that passes 25 minutes is stopped ([slug compiler](https://devcenter.heroku.com/articles/slug-compiler)). Then Heroku boots new dynos on the new release, however small the change. After the first deploy, Elements never rebuilds from scratch on the server. A project server runs there, the same kind you run locally, and it carries its build state over from one deploy to the next. So `elements deploy` behaves less like `git push` and more like a sync: over SSH, the two project servers find the files that differ, typically a few KB, and the server reworks only what those files reach. There are no dynos to cycle. An atomic directory rename puts the new release live with no restart pause and no warm-up. On a new machine the first deploy needs a few seconds; after that, under a second is usual ([what deploy does](/learn/man/deploy/what-happens)). ## Tests and Migrations Are One Deploy, Not Two Steps A release that changes the schema has to pass its tests against the new schema before production changes. On Heroku, tests and migrations are two separate steps. Heroku CI runs your tests when you push to GitHub, on a Performance-M dyno by default ([Heroku CI](https://devcenter.heroku.com/articles/heroku-ci)). The release phase runs a command, typically migrations, against your real database, and a failure leaves the current release in place ([release phase](https://devcenter.heroku.com/articles/release-phase)). Elements folds the two into one deploy, in an order that protects your data. Every machine compiles the release, the first machine migrates a separate test database, and every machine then runs your tests against that migrated schema. The app database is migrated after the tests pass, on the first machine, with all pending migrations in a single transaction. If the compiler, a test or a migration fails, there is no release, and the app keeps serving the one before ([deploy](/learn/man/deploy), [tests](/learn/man/tests), [migrations](/learn/man/migrations)). The tests run on the server you already rent, against the exact build being released. ## Jobs, Cron and Realtime Without Add-Ons A Heroku app commonly does background work with a worker dyno reading from a queue in a Redis add-on, and scheduled work with the Scheduler add-on. Heroku's own documentation says Scheduler "is known to occasionally (but rarely) miss the execution of scheduled jobs" and recommends a custom clock process when scheduled jobs are critical ([Scheduler](https://devcenter.heroku.com/articles/scheduler)). Elements has no clock process to write and no add-on to attach. Jobs and cron are part of the framework, so they go out with every deploy. A job lives as a row in a Postgres queue table, which means enqueueing one inside a transaction ties its fate to that transaction: commit and it runs, roll back and it never existed. Schedules sit in the project's `index.ts`, and for each tick Postgres elects a single machine to fire it ([jobs](/learn/man/jobs), [transactions](/learn/man/database/transactions)). Realtime updates, like a chat or a live dashboard, travel over Postgres `LISTEN` and `NOTIFY`, so they need no Redis either ([channel](/learn/man/channel), [livetable](/learn/man/livetable)). Because the queue and the cron leader live in Postgres, more servers pointed at one shared Postgres share the traffic and the jobs, and each cron tick still fires once ([load balancer](/learn/man/deploy/load-balancer)). ## Your Agent Finds the Broken Build Before `git push` An agent is only as good as its correction loop: how soon it learns what it got wrong. On Heroku, a job queue, a cache and scheduling each come from an add-on, and each brings its own config vars, its own client and its own docs, on top of buildpacks, the Procfile, dyno types and the release phase. Each one is another interface for the agent to look up and another joint where the app and the platform can come apart. Some plumbing between code and server is necessary, and past it each add-on costs an agent more to learn and wire than it saves. On Heroku, a build error appears in the build output after `git push`. Your agent reads it there, fixes the code, and pushes again. With Elements, the push is not where you find out. The project server holds the build graph and build state in memory as you work, and on every save the build state is updated almost instantly: it builds in milliseconds and answers agents and humans in microseconds ([build](/learn/man/build)). Your agent checks with `elements build -json` after an edit and sees every build error at once, failing tests, migration problems and the findings of type checking and program analysis, locally, so the build the server later runs is one that has already passed. `elements deploy -json` then reports every result from every server in the same shape. The jobs, cron and database that Heroku attaches as add-ons are part of one coherent system here, built on SSH, Ubuntu and Postgres, so the same build checks them too. ## Heroku Moved to Sustaining Engineering In February 2026 Heroku announced it is "transitioning to a sustaining engineering model focused on stability, security, reliability, and support," "rather than introducing new features," and that Enterprise contracts are no longer offered to new customers. Customers who pay by credit card, existing and new, keep the same pricing, billing and service ([An Update on Heroku](https://www.heroku.com/blog/an-update-on-heroku/)). An Elements app is tied to no host: any Ubuntu server you reach over SSH can run it. ## Changing Servers Is a Config Change Leaving Heroku means replacing its add-ons, pipeline and Scheduler on the new host. Leaving one Elements host for another replaces no service, because the build, the tests, the jobs and the deploy travel inside the project. Rent the next server, write its address into `config.jsoc` and run the same one command as before ([walkthrough](/learn/man/deploy/walkthrough)); `elements db dump` exports bundled Postgres data as SQL for the new machine to load ([database cli](/learn/man/database/cli)). What you carry is TypeScript, HTML, CSS and SQL, run on Node over standard Postgres, and runtime packages such as `@elements/app` are open source under MIT ([licenses](/learn/man/licenses)). ## Questions ### What is the difference between Elements and Heroku? Heroku is a hosting platform: it builds your app on git push, runs it on dynos, and bills each dyno and add-on. Elements is not a host. It keeps the one-command deploy, and that command sends your app, jobs, cron and Postgres to a server you rent from a provider of your choosing. ### Is Elements a Heroku alternative? Yes, and you keep the single deploy command. elements deploy builds, tests and releases the app onto a server you rent, and that server replaces the dynos and add-ons: it runs the app, Postgres, the job worker, cron, and a load balancer that issues and renews TLS certificates. ### Is Heroku shutting down? Heroku has not announced an end date. In February 2026 it moved to what it calls a sustaining engineering model, focused on stability, security, reliability and support, and stopped offering Enterprise contracts to new customers ([An Update on Heroku](https://www.heroku.com/blog/an-update-on-heroku/)). ### How much does Elements cost compared to Heroku? Heroku bills each dyno and each add-on, including dynos that sit idle, up to each plan's monthly price ([Heroku pricing](https://www.heroku.com/pricing)). Elements charges a price per machine, per month, with no per-process or per-add-on charge ([Elements pricing](/pricing)), and the server is rented from the provider you choose. ### How do I move a Heroku app to Elements? Rent an Ubuntu server, create an Elements project, and have your agent port the app's routes, pages and queries into it. npm packages install as is, so the libraries a Node app already uses come along. Your data is standard Postgres, so it moves to the Postgres Elements runs on the server, or to any managed Postgres you point DB_HOST at.