Skip to main content

Compute

A Compute resource defines a virtual machine (or future cluster). inforge expands a single spec into one or more VM instances based on instance_count.

A compute resource lives in a folder under regional/compute/<name>/:

regional/compute/bridge/
manifest.yaml # required — the compute spec
cloud-init.sh # optional — cloud-init script sidecar

Schema

manifest.yaml:

name: bridge # required
instance_count: 1 # optional — defaults to 1; expands into bridge-01..bridge-NN
container: bridge # required
provider: hetzner # optional — inherits from inforge.yaml providers.compute if omitted
network: ingress # required — name of the Network resource to join
size: SMALL # required — resolved against the size table
image: ubuntu-24.04 # required — canonical OS image name
cloud_init: cloud-init.sh # optional — path relative to the compute folder
kind: vm # optional — "vm" (default) | "cluster" (reserved)
deploy_user: # optional — SSH deploy account provisioned at VM-init time
name: deploy
firewall: # optional — declarative inbound rules; omit to use defaults
inbound:
- proto: tcp
port: 80
- proto: tcp
port: 443

Fields

FieldTypeRequiredDescription
namestringYesResource name.
instance_countintNoHow many VMs to create (default 1). Each gets a specKey name-01name-NN.
containerstringYesGrouping label.
providerstringNoProvider name (e.g. hetzner). Inherits from inforge.yaml providers.compute if omitted; explicit value takes precedence. A validation error is raised if neither is set.
networkstringYesName of the Network resource to attach this VM to.
sizestringYesSize name from the size table: SMALL, MEDIUM, or LARGE.
imagestringYesOS image. See supported images.
cloud_initstringNoPath to a cloud-init script, relative to the compute folder (e.g. cloud-init.sh). Absolute paths are also accepted.
kindstringNovm (default; built) or cluster (k8s; reserved).
deploy_userobjectNoDeploy user provisioned at VM-init time. See Deploy user.
firewallobjectNoDeclarative inbound firewall rules. See Firewall rules.

Size table

The size table is the cloud-agnostic set of valid size names — SMALL, MEDIUM, LARGE by default. It carries no cpu/memory; each provider maps a size name to a concrete machine type in its region realization.

Override by placing sizes.yaml in resources/<env>/. The file is a YAML list of names and replaces the default table wholesale:

resources/prd/sizes.yaml
- SMALL
- MEDIUM
- LARGE
- XLARGE

Supported images

ValueDescription
ubuntu-24.04Ubuntu 24.04 LTS
ubuntu-22.04Ubuntu 22.04 LTS
debian-12Debian 12

Deploy user

When deploy_user is set, inforge provisions the named account at VM-init time by appending a first-boot step to the cloud-init script. The step creates a login-shell user, installs the authorized key from ssh.deployPublicKey in variables.yaml, and grants passwordless sudo.

deploy_user:
name: deploy
FieldTypeRequiredDescription
namestringYesUsername for the SSH deploy account.

The SSH key material is not set here — it comes from ssh.deployPublicKey in variables.yaml. This keeps the key in one env-level location so rotating it only requires updating variables.yaml and re-running inforge deploy.

Cloud-init templates

Cloud-init scripts support these placeholders:

PlaceholderDescription
{{domain}}Fully-qualified domain for this VM instance
{{deploy_public_key}}Deploy user's SSH public key (from ssh.deployPublicKey)
{{deploy_user}}Deploy user name (from deploy_user.name; empty if not declared)
{{instance}}Instance number (integer)
{{manifest}}Assembled service manifest (YAML, secret-free)

inforge appends one first-boot step automatically to every cloud-init script: user provisioning (creates the deploy user when declared). Secrets are not a first-boot concern — each service fetches its own at runtime via inforge-bootstrap.

Firewall rules

The inbound rule set is derived, not hand-maintained:

  • SSH (22) is always permitted (management access is never locked out).
  • Every service's ingress listen port on this host is opened automatically, plus :80 when the host terminates TLS (nginx serves the ACME HTTP-01 challenge there).
  • Any rules in the firewall.inbound block are unioned on top — for raw ports not fronted by nginx (a port a service binds directly, with no proxy).

Outbound traffic is always fully allowed. So you only declare a firewall.inbound rule for a raw public port; ports behind nginx ingress open themselves.

regional/compute/bridge/manifest.yaml
name: bridge
container: bridge
provider: hetzner
network: ingress
size: SMALL
image: ubuntu-24.04
firewall:
inbound:
# A raw UDP port a service binds directly (not nginx ingress).
- proto: udp
port: 51820

Firewall rule fields

FieldTypeRequiredDescription
protostringYesProtocol: tcp, udp, or icmp.
portint or stringYesPort number (80) or range (8000-9000). Not used when proto is icmp.

Example

regional/compute/bridge/manifest.yaml
name: bridge
instance_count: 1
container: bridge
network: ingress
size: SMALL
image: ubuntu-24.04
cloud_init: cloud-init.sh
deploy_user:
name: deploy
regional/compute/bridge/cloud-init.sh
#!/bin/bash
set -euo pipefail

# Write the service manifest (inforge substitutes {{manifest}} at provision time).
mkdir -p /etc/wardnet
cat > /etc/wardnet/manifest.yaml << 'EOF'
{{manifest}}
EOF

inforge appends the deploy-user provisioning step automatically — no need to write useradd or key-installation logic in your cloud-init template. Secrets are not a first-boot concern; each service fetches its own at runtime via inforge-bootstrap.

Outputs

OutputDescription
publicIPPublic IPv4 address of the VM