Walkthrough
elements man deploy/walkthrough Read as markdownFrom zero to a deployed app on a fresh machine.
Walkthrough: From Zero to Deployed
The most common path: a single Digital Ocean droplet. Adapt for AWS, Linode, Hetzner, or any provider that gives you Ubuntu over SSH.
Each step here costs money or modifies infrastructure. Confirm with the user before running anything destructive.
1. Machine
Create an Ubuntu 24.04 LTS box at your provider. Note the public and private
IPs. Make sure your SSH key is authorized on the machine and that you can
ssh ubuntu@<public-ip> (or root@, depending on the provider).
A small droplet is fine to start.
2. Database
Nothing to provision. Elements brings up the bundled Postgres cluster on the box
during the first deploy. The scaffolded production.env already sets
DB_HOST=127.0.0.1, so the app talks to that local cluster out of the box.
If you'd rather use a hosted database, provision it now and copy its connection
params into production.env instead.
3. Domain
You can reach the app two ways. With no domain, the app is available directly at
http://<public-ip> over plain HTTP. With a domain, Elements provisions SSL
automatically and serves the app at https://<your-domain>.
To use a domain, register one with any registrar (Namecheap, Cloudflare
Registrar, Porkbun, Hover, etc.) and point an A record at the machine's public
IP.
A example.com <public-ip>
For a single-machine deploy that is all you need. With multiple machines in the environment, you can point DNS at any one of them or list several records for DNS-level round-robin and failover. Every machine in the cluster runs its own load balancer and round-robins requests to the other machines, so traffic gets distributed across all of them even with one DNS record.
You configure the domain in step 4 under deploy.domain, as one host
(domain: "example.com") or a list (domain: ["example.com", "www.example.com"]). When domain is set, Elements issues and renews SSL
certificates for those hosts automatically via Let's Encrypt. You do not need
to provision certificates yourself, set up a renewal cron, or configure HTTPS
anywhere else.
4. Config
config.jsoc:
{
database: {
name: "my_app",
host: env("DB_HOST", "127.0.0.1"),
port: env<number>("DB_PORT", 5433),
user: env("DB_USER", "postgres"),
password: env("DB_PASSWORD", ""),
},
session: {
expires: "30d",
},
deploy: {
domain: "example.com",
ssh: { user: "ubuntu", identityFile: "~/.ssh/id_ed25519" },
env: {
production: {
machines: [
{ name: "production0", publicIp: "<public-ip-from-step-1>", privateIp: "<private-ip-from-step-1>" },
],
},
},
},
}
config/env/production.env (gitignored):
DB_HOST=127.0.0.1
That is the production database configuration for the bundled cluster. If you
provisioned a hosted database in step 2, set DB_HOST,
DB_USER, and DB_PASSWORD to its values instead.
5. Deploy
elements deploy
The first deploy provisions the machine: installs Elements, creates the system user, sets up systemd, brings up the Postgres cluster, installs the license, configures the built-in load balancer, and provisions SSL via Let's Encrypt. Subsequent deploys transfer only what changed. The local project server speaks directly to the deploy machine's project server over the SSH tunnel using a peer-to-peer protocol, and only the files that changed are transferred. An incremental build runs on the deployment machine, and typical incremental deploys complete in under a second.
6. Verify
The deploy verifies itself. It exits non-zero and prints diagnostics if any
phase failed, and an empty diagnostics array means the release is live. Open
https://example.com in a browser to see it.
elements db -connect=production # psql against the production db
There is no health check to run afterward and no machine to log into. See What Deploy Does for the diagnostics and how to read them.