Skip to main content
Running a universe is three phases. Terraform owns the first, the unoverse CLI the second, and these runbooks cover the third:
  1. Provision β€” your ground (infra/digitalocean or infra/aws) creates the VM, load balancer, TLS certificate, firewall, Postgres, and Redis, and renders the complete production configuration. ./unoverse ground prefills its input file from your cloud CLI.
  2. Deploy β€” unoverse deploy init the first time, unoverse deploy after that.
  3. Operate β€” database, hardening, health, restarts: the runbooks below.

Provision (Terraform)

Everything infrastructure is the ground’s job and never a runbook’s: TLS (DO managed Let’s Encrypt / AWS ACM at the load balancer β€” no proxy software on the VM), DNS records, the cloud firewall (SSH and Dozzle admin-IP-only), Postgres (fresh, adopted, or BYO β€” see 02-database), and Redis (always provisioned, TLS).

Sizes

size in terraform.tfvars scales the box and the stores, never the topology (all sizes are single-VM). When multi-VM Active-Active arrives it will scale the app tier only: UMAP stays one shared service (UMAP_SERVICE_URL), because spatial coordinates are only comparable through the same trained model instance.

External Dependencies

Supported Platforms

  • Cloud grounds: DigitalOcean (infra/digitalocean), AWS (infra/aws)
  • On-prem: any Ubuntu 22.04+ / Debian 12 VM β€” you own firewall and TLS, then Deploy and Operate are identical

Deploy (the CLI)

Each phase of init stays available on its own for re-runs: deploy db, deploy test. The CLI reads the deploy target from your ground’s rendered configuration and generates a temporary Ansible inventory on every run, so there is no inventory file to maintain. Your own work (nodes, design, prompts) never rides a deploy: it arrives via unoverse update (git), the Marketplace (per item, database-driven), or Studio publish.

Operate (the runbooks)

Logs need no runbook: Dozzle runs by default at http://<VM_IP>:8080 (admin-IP-only via the cloud firewall), streams straight from the Docker socket, and stores nothing. Log growth is capped by json-file rotation (10 MB Γ— 3 per service) in docker-compose.yml. Enterprise ships logs to its own SIEM by pointing the Docker logging driver there instead.

Environment: One File You Write, One You Don’t

.env is yours β€” local development only. Copy .env.example, set localhost Postgres, Redis, your OpenAI key, and your OIDC values (or AUTH_ENABLED=false). Docker compose reads it automatically. Gitignored. Production configuration is not a file you touch. Your terraform.tfvars is the single input; everything downstream is machine-managed:
The rendered env lands at .env.production (gitignored) as a deploy artifact β€” unoverse deploy re-renders it from your applied ground whenever it’s missing. Two things are worth knowing about it, and only two:
  • It holds the master CREDENTIAL_ENCRYPTION_KEY. Keep a safe copy with your database backups: a database backup is unreadable without it.
  • Never edit it. To change any production value, edit terraform.tfvars, terraform apply, delete the file, and redeploy.
How DOMAIN drives Canvas URLs: When DOMAIN=yourdomain.com is set, docker-compose.yml automatically derives:
  • VITE_API_URL=https://api.yourdomain.com
  • VITE_SERVER_WS_URL=wss://api.yourdomain.com
When DOMAIN is unset (local dev), set API_URL=http://localhost:4105 in .env β€” Canvas calls the platform’s public listener (unoverse :4105) directly.

Prerequisites

  • Terraform 1.5+ and your cloud CLI (doctl or aws) on your machine
  • Ansible installed locally (pip install ansible)
  • DOCR token for pulling images (from your Unoverse admin)