# Setup What the deploy machine needs, the database that runs on it, and the config that describes both. ## Prerequisites - An Ubuntu box with SSH access, on the latest Ubuntu release your provider offers. A new machine is the usual starting point. Any size works, including a small Digital Ocean droplet. - A domain name pointed at the machine's public IP, if you want HTTPS. Optional. Without a domain the app is reachable directly at `http://` over plain HTTP. The database comes with Elements. On first deploy, Elements installs and starts the bundled Postgres cluster on the machine (data directory `/elements/db`), so you do not provision a database separately. To use a hosted Postgres, or one shared cluster for several machines, set `DB_HOST` in the environment's env file. You give Elements the public and private IPs of the machine and SSH credentials. Elements takes care of the rest, including provisioning, the system user, systemd, the Postgres cluster, the load balancer, automatic SSL, and the deploy itself. ## Database on the Deploy Machine By default the deploy machine runs the same bundled Postgres cluster as development. The per-machine `elements-machine` daemon manages it on the box, shared across projects, restarted before the project server, with its data under `/elements/db`. On production, migrations are forward-only. The scaffolded `config.jsoc` sets `host: env("DB_HOST", "127.0.0.1")`, and the scaffolded `production.env` sets `DB_HOST=127.0.0.1`, so a production deploy talks to the bundled cluster on the box with no extra configuration: ``` # config/env/production.env DB_HOST=127.0.0.1 ``` To use a remote or hosted Postgres (Digital Ocean Managed DB, AWS RDS, Neon, Supabase, Railway, Crunchy Bridge, …), point `DB_HOST` (and `DB_PORT`, `DB_USER`, `DB_PASSWORD`, and the app-user variants) at that server. Use Postgres 16 or newer. That connection is encrypted with TLS. For verified TLS (`verify-full`, what HIPAA deployments use), commit your provider's CA certificate to the project and point `DB_SSL_ROOT_CERT` at it: ``` # config/env/production.env DB_HOST=mydb.abc123.us-east-1.rds.amazonaws.com DB_SSL_ROOT_CERT=config/certs/db-ca.pem ``` The certificate deploys with the project, and every machine verifies the server against it. See `elements man database`. ## Several Machines To run several machines, point every machine's `DB_HOST` at one shared Postgres cluster, on a machine of your own or at a hosted provider. Every feature then works across all of them with no other change: live tables, channels, sessions, jobs, and cron. A write on any app server reaches watching browsers on every app server, because broadcasts travel through the shared database, and each cron tick runs on one machine. Elements creates the app and test databases on the shared cluster at the first build. Uploaded files go in Postgres, where every machine reads them, or in S3 or any object store. See `elements man recipes/file-upload`. ## The Config ```jsoc // config.jsoc { deploy: { domain: "example.com", // or ["example.com", "www.example.com"] ssl: true, // default true when domain is set ssh: { user: "ubuntu", identityFile: "~/.ssh/id_ed25519", }, env: { production: { machines: [ { name: "production0", publicIp: "203.0.113.10", privateIp: "10.0.0.10" }, ], }, staging: { domain: "staging.example.com", machines: [ { name: "staging0", publicIp: "203.0.113.20", privateIp: "10.0.0.20" }, ], }, }, }, } ``` `domain` takes one host or a list of them, and every host in it goes into the TLS certificate. Machine names are zero-indexed: the first machine in an environment is `production0`, the second `production1`, and so on. `publicIp` is where Elements connects, and is required. `privateIp` is for machine-to-machine traffic in a multi-box cluster; leave it out for a single machine or if you don't have it. Run: ```bash Terminal elements deploy # default: production elements deploy staging # named environment ```