unoverse deploy drives the first two, and these runbooks cover the third:
- Provision: your ground (
infra/digitaloceanorinfra/aws) creates the VM, load balancer, TLS certificate, firewall, Postgres, and Redis, and renders the complete production configuration. - Deploy: the platform is installed onto what was built, migrations included.
- Operate: database, hardening, health, restarts: the runbooks below.
Provision (Terraform, driven by the CLI)
terraform.tfvars, plans, and applies what you approve. Terraform is still the
interface underneath, and a platform team can drive it directly instead
(cd infra/digitalocean && terraform init && terraform apply); deploy then picks up from
the applied ground.
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 by default; see 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)
unoverse deploy db and unoverse 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 through unoverse deploy studio, live immediately, or from the marketplace, one item at a time.
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:
terraform output -raw env_production), ships it, and deletes it. Two things are worth knowing, and only two:
- Your ground holds the master
CREDENTIAL_ENCRYPTION_KEY. Terraform generates it and keeps it in state, so back up your Terraform state alongside your database. A database backup is unreadable without that key, and no backup can bring it back. - There is nothing to edit. To change any production value, edit
terraform.tfvarsand rununoverse deploy <cloud>again. It re-renders in full.
DOMAIN drives canvas URLs:
When DOMAIN=yourdomain.com is set, docker-compose.yml automatically derives:
VITE_API_URL=https://api.yourdomain.comVITE_SERVER_WS_URL=wss://api.yourdomain.com
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)

