Deploy to production
This guide brings up the committed prod environment on your Mac: two public static nodes, no Lima, no roaming hosts in the inventory.
| Host | Role |
|---|---|
| static-1 | Ubuntu 26.04 public endpoint (mesh hub, x86_64) |
| static-2 | Ubuntu 26.04 public endpoint (x86_64) |
Optionally, add a roaming node later with Adding a roaming node—after statics are up.
Step 1: Configure prod hosts
Section titled “Step 1: Configure prod hosts”Set hostname and fill public IPs and SSH fingerprints for both static nodes.
Edit inventories/prod/hosts.yml:
- Set
all.vars.hostname(for exampleexample.com). Nameserver, API, app, and app URLs are derived from that name. - Fill placeholders for static-1 and static-2 (public IP and SSH host-key fingerprint). Install this public key on each VM with static IP (
opsuser)—same prep as Adding a public node. Go and do that first.
---all: vars: project: example hostname: example.com
nodes: hosts: static-1: public_ip: "203.0.113.11" ssh_ed25519_sha256: "SHA256:pRR...e+M" private_address: 10.217.79.11 static-2: public_ip: "203.0.113.12" ssh_ed25519_sha256: "SHA256:0f37...2ed230" private_address: 10.217.79.12---all: vars: project: <project> hostname: <your-prefix>.<project>.com
nodes: hosts: static-1: public_ip: "REPLACE_WITH_STATIC_1_PUBLIC_IPV4" ssh_ed25519_sha256: "SHA256:REPLACE_WITH_COMPLETE_FINGERPRINT_STATIC_1" private_address: 10.217.79.11 static-2: public_ip: "REPLACE_WITH_STATIC_2_PUBLIC_IPV4" ssh_ed25519_sha256: "SHA256:REPLACE_WITH_COMPLETE_FINGERPRINT_STATIC_2" private_address: 10.217.79.12Step 2: Configure DNS
Section titled “Step 2: Configure DNS”Create records for the hostname you set in Step 1. Supercompute derives the rest by adding the hostname as a common suffix. Leave example.com at your registrar. If the DNS is hosted on Cloudflare, do not enable the proxy orange icons.
| Prefix | Name | Type | Value |
|---|---|---|---|
ns |
ns.example.com |
A | static-1 public IPv4 |
ns |
ns.example.com |
A | static-2 public IPv4 |
sc-api |
sc-api.example.com |
CNAME | ns.example.com |
sc-app |
sc-app.example.com |
CNAME | ns.example.com |
Later, dynamic app URLs use the same nameserver:
| Prefix | Name | Type | Value |
|---|---|---|---|
apps |
apps.example.com |
NS | ns.example.com |
Supercompute’s Nameserver then serves *.apps.example.com. If the VM IPs are not ready yet, add the A records before bring-up.
To use different API or app hostnames, edit dns_prefix_api and dns_prefix_app (and other dns_prefix_* values if needed) in inventories/<env>/group_vars/all/main.yml, then create matching DNS records and re-run task up.
Step 3: Prepare the two public VMs
Section titled “Step 3: Prepare the two public VMs”Install this public key on both production VMs (ops user)—follow Adding a public node for each host.
Install Ubuntu on each VM, create the ops user, and open bootstrap firewall ports.
For static-1 and static-2:
- Ubuntu 26.04 amd64 + public IP (matches
provider.imageinmain.yml). - Create
ops, install~/.ssh/example-prod.pub, passwordless sudo. - Record each host’s
SHA256:…from/etc/ssh/ssh_host_ed25519_key.pub. - Fill
public_ipandssh_ed25519_sha256inhosts.yml. - Firewall: TCP 22 from your Mac
/32during bootstrap. Open UDP 51830 broadly if you plan to add roaming nodes later (Adding a roaming node).
Prove SSH:
ssh -i ~/.ssh/example-prod -o IdentitiesOnly=yes ops@STATIC_1_IP truessh -i ~/.ssh/example-prod -o IdentitiesOnly=yes ops@STATIC_2_IP trueStep 4: Initialize the vault
Section titled “Step 4: Initialize the vault”Create the encrypted vault and local password file for prod.
task vault-init ENV=prodCreates inventories/prod/.vault-pass and encrypted vault.yml with vault_meta, placeholder vault_database_url, and auto-generated vault_database_secret. The first task up adds WireGuard keypairs.
Decrypted inventories/prod/group_vars/all/vault.yml looks like this:
---vault_meta: project: example provider: prodvault_database_url: postgresql://REPLACE_WITH_USER:PASSWORD@HOST:5432/DATABASEvault_database_secret: mT4nQ8pL2vK6wB9xF1hJ5cY0aZ3dE7gH_rXv---vault_meta: project: <project> provider: <env>vault_database_url: postgresql://REPLACE_WITH_USER:PASSWORD@HOST:5432/DATABASEvault_database_secret: <auto-generated at vault-init>PostgreSQL database
Section titled “PostgreSQL database”Point the vault at your external Postgres before the first deploy.
Supercompute needs a Postgres database with owner role hosted outside of the Supercompute cloud.
task vault-edit ENV=prodtask vault-edit decrypts inventories/prod/group_vars/all/vault.yml in your editor. You should see vault_meta, vault_database_url, and vault_database_secret (WireGuard keys appear after the first task up). Replace only vault_database_url with your connection URI (same shape as the prod tab). Leave everything else unchanged.
Example after you set the database URL:
---vault_meta: project: example provider: prodvault_database_secret: mT4nQ8pL2vK6wB9xF1hJ5cY0aZ3dE7gH_rXv---vault_meta: project: <project> provider: <env>vault_database_url: postgresql://REPLACE_WITH_USER:PASSWORD@HOST:5432/DATABASEvault_database_secret: <auto-generated at vault-init>Save and exit the editor:
- vim/vi (default on macOS): press
EscapeEscEscapeEsc , type:wq, pressEnterEnterEnterEnter - nano: press
Control + OControlOControl + OControlO ,EnterEnterEnterEnter , thenControl + XControlXControl + XControlX
task up rejects the vault-init placeholder in vault_database_url.
Step 5: Bring up the cloud
Section titled “Step 5: Bring up the cloud”Deploy the WireGuard mesh and cluster stack to both static nodes.
task up ENV=prodWhen it asks for BECOME password, enter your macOS password. You can run task up multiple times to retry; it is idempotent.
inventories/prod/group_vars/all/main.yml already sets control_plane: mac, provider.platform: public, Ubuntu 26.04 amd64 (source: template:ubuntu-26.04), and production-sized resources. Change provider.image.source only if your provider automation requires a different image identifier.
Step 6: SSH to the nodes
Section titled “Step 6: SSH to the nodes”Confirm you can reach both nodes over the mesh.
task wg-status ENV=prodtask ssh ENV=prod NODE=static-1task ssh ENV=prod NODE=static-2Optional latency check from a node to the Mac mesh peer:
ping -c 10 -I scwg0 10.217.79.1If you can access both nodes, you are done with this guide.
Checklist
Section titled “Checklist”| Item | Done |
|---|---|
hostname set in hosts.yml (for example example.com) |
|
Parent DNS: ns A per static + api/app CNAME to ns + apps NS (Cloudflare proxy off) |
|
static-1 + static-2 public IPs + fingerprints in hosts.yml |
|
~/.ssh/example-prod on both VMs (ops user) |
|
vault_database_url set in prod vault (not placeholder) |
|
ssh … true works for both static IPs |
|
task ssh ENV=prod NODE=… works for both statics |
Backup
Section titled “Backup”Store off-repo: ~/.ssh/example-prod (+ .pub), inventories/prod/.vault-pass, and a copy of inventories/prod/group_vars/all/vault.yml.
Next steps
Section titled “Next steps”- Adding a roaming node
- Deploy with GitHub Actions — same
prodinventory withcontrol_plane: gha