Deploy

00 Markdown

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.jsoc
deploy: { 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

Terminal
elements 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:

  1. 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.
  2. 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.
  3. Build on every server. Each server builds your app, rebuilding only what the changed files affect.
  4. Migrate the test database. New migrations are applied to the environment's test database first.
  5. 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.
  6. Migrate the app database. Only once every server has passed do the migrations reach your app's real data.
  7. 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 -json it 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 Chat
Deploy 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.jsoc
machines: [ { 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:

Terminal
elements 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).

Get a digest to your inbox once per week.

Comments · 0