Deploying is built into Elements. One command on your laptop, elements deploy, builds, tests, migrates and releases your app across every server in
your cluster, on servers you control, rented from any provider. There is no CI
service to build and test it, no pipeline in your Git host to trigger it and no
hosting platform to run it. Elements sets up each server for you, with its
database, its load balancer and, once you add a domain, its HTTPS certificate. The first deploy to a
new server takes a few seconds. Every deploy after that sends only what changed
and is live in a few hundred milliseconds, and it goes live only if your app
builds and its tests pass on every server.
What you need
- One or more servers running Ubuntu Linux, from any provider, that you can log in to over SSH. A small virtual server from DigitalOcean (opens in a new tab), for example, works well.
- Each server's public IP address. Your provider shows it when you create the server. With more than one server, you also need each one's private IP address.
- A domain name, if you have one. It is optional.
IP addresses, and an optional domain name
The public IP address, a number like 203.0.113.10, is how the internet
reaches your server: your laptop deploys to it, and visitors open your app at
it. It is the one address Elements always needs.
With just that address, your app is live at http://203.0.113.10 as soon as
you deploy. Send that address to a friend and they can open your app. That is
the quickest way to get something online, and the way we recommend starting.
An address like that is plain HTTP, so browsers mark the page "Not secure". If
you have a domain name, like example.com, point it at your server's public IP
address with a DNS record at your domain registrar, and add it to your config.
Elements then gets an HTTPS certificate for it from Let's Encrypt
automatically, and renews it, so your app is served securely at
https://example.com. You can add a domain whenever you are ready, and deploy
again.
1. Tell Elements about your servers
Add a deploy section to config.jsoc with how to log in, and your server's
public IP address from your provider:
config.jsocdeploy: { ssh: { user: "ubuntu", identityFile: "~/.ssh/id_ed25519", }, env: { production: { machines: [ { name: "production0", publicIp: "203.0.113.10", }, ], }, }, },
When you have a domain name, add it at the top of the deploy section, as
domain: "example.com", and deploy again.
Secrets your app needs in production, such as API keys and passwords, go in
config/env/production.env. That file is never committed.
2. Deploy
Terminalelements deploy
That runs from your laptop, and it is the deploy for every server in the environment. The first deploy to a server sets it up: Elements installs itself, creates a system user for your app, starts Postgres, starts the load balancer and, if you have set a domain, gets an HTTPS certificate for it from Let's Encrypt. You do not install or configure any of it yourself.
What happens when you deploy
One elements deploy on your laptop runs the deploy on every server in the
environment at once, as one coordinated operation. Each step completes on
every server before the next step starts on any of them, and an error on any
server stops the deploy everywhere with nothing changed:
- Build and test on your laptop. The deploy starts only if your app builds cleanly and its tests pass where you are. A build error or a failing test on your laptop stops it before anything is sent.
- Upload to every server. Elements on your laptop and Elements on each server compare your files directly, and only the files that changed are sent to each one.
- Build on every server. Each server builds your app, rebuilding only what the changed files affect.
- Migrate the test database. New migrations are applied to the environment's test database first.
- Test on every server. Every server runs your tests against that test database. One failing test on one server stops the deploy for all of them.
- Migrate the app database. Only once every server has passed do the migrations reach your app's real data.
- Release on every server. All the servers switch to the new release together, each in one atomic step. The old release serves every request until the new one is completely ready, so a visitor never sees a half-deployed app.
Every server moves together
Your servers never drift apart. Any build error, local or remote, stops the release across the cluster, on every server, and a failing test is a build error. None of the servers releases until your laptop's build and tests have passed and every server has uploaded, built and passed its tests, so they all go from the old release to the new one in the same step, and a failure in any step before the release leaves every server running the release it already had. Two servers or ten, the deploy is coordinated from the one command on your laptop, with nothing to orchestrate yourself.
There is no CI service to set up. The tests that run on your laptop as you work are the same tests that run on every server before it goes live.
Licenses are handled for you
Your contract with Elements holds a pool of licenses, and the Elements on your laptop is running under that contract. When you deploy, it checks out a license from that same pool, from the Elements license servers, for each server you deploy to. There is nothing to activate on the servers themselves.
If your pool does not have enough licenses for every server, the deploy does
not go ahead. It stops with an error that says so and prompts you to add more:
run elements purchase, which opens your account page, where you can add
licenses. Then deploy again.
Why an agent needs a deploy system
It is fair to ask why an AI agent cannot just deploy an app itself. It can type commands, after all. But deploying a real app is not one command. It is copying code to each server, building it there, applying migrations in the right order, running the tests, switching every server to the new release at the same moment, and undoing all of it if any step fails. Done by hand over SSH, that is dozens of commands across several machines, and a mistake partway through leaves some servers on the new release and some on the old, with no clean way back.
That work needs a system underneath it that does every step the same way every time. Usually that system is several services stitched together: a CI service to build and test, a pipeline in your Git host to trigger it, a hosting platform to run it. Elements does all of it from one command on your laptop, across your cluster, with nothing in between. Here is what that command gives your agent:
- Safety. A deploy goes live only if your app builds and its tests pass, on your laptop and on every server. Anything less and the running app is left untouched, so a mistake your agent makes never reaches your visitors.
- Speed. A deploy after the first is live in a few hundred milliseconds. Your agent can release each change on its own and know at once whether it worked.
- Diagnostics. The deploy says whether it worked and, if not, why. With
-jsonit returns one result covering your laptop and every server, with each error naming the server it happened on. Your agent reads it, fixes the problem and deploys again, without anyone logging in to a server. - Coordination. One command moves every server in the cluster through each step together, and they release together or not at all. Your agent never has to manage servers one by one.
To have your agent deploy your app, paste a prompt like this into an agent session running in your project directory, with your own server addresses and domain:
Agent ChatDeploy the app. 203.0.113.10 10.0.0.10 example.com
A load balancer and HTTPS on every server
Every server runs Elements' own load balancer. It gets a certificate from Let's Encrypt for each of your domains, renews it before it expires, and sends plain HTTP visitors to HTTPS. There is no web server, proxy or certificate tool to install.
One server or many
For a production app you can usually start with one server, but we recommend two, for redundancy and performance. If one goes down, or you take it down to resize or replace it, the other keeps serving your visitors, and in normal running the two share the load. Load balancing between them happens automatically.
On one server, your app uses the Postgres that Elements runs on that server. A
cluster of more than one server needs a hosted Postgres instead, so that every
server reads and writes the same data. We recommend
DigitalOcean's managed Postgres (opens in a new tab).
Point your app at it by setting DB_HOST and the other database values in
production.env, as the database manual page (opens in a new tab) shows.
Then add servers to the machines list, this time with each server's private
IP address as well. The private address only works inside your provider's
network, and it is how your servers pass requests to each other:
config.jsocmachines: [ { name: "production0", publicIp: "203.0.113.10", privateIp: "10.0.0.10", }, { name: "production1", publicIp: "203.0.113.11", privateIp: "10.0.0.11", }, ],
Deploy again. Every server's load balancer knows about the others and spreads requests across all of them over their private addresses, so you can point your domain at one server or at several. If a server stops responding, requests go to the others.
Staging and other environments
production is the default environment. Add another, such as staging, with
its own servers in config.jsoc and its own config/env/staging.env, and
deploy to it by name:
Terminalelements deploy staging
Commands also run against a deployed server without logging in to it. For
example, elements db -remote=production opens a SQL prompt on the production
database.
The manual: deploy (opens in a new tab), config (opens in a new tab).