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
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Resource name. |
instance_count | int | No | How many VMs to create (default 1). Each gets a specKey name-01 … name-NN. |
container | string | Yes | Grouping label. |
provider | string | No | Provider 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. |
network | string | Yes | Name of the Network resource to attach this VM to. |
size | string | Yes | Size name from the size table: SMALL, MEDIUM, or LARGE. |
image | string | Yes | OS image. See supported images. |
cloud_init | string | No | Path to a cloud-init script, relative to the compute folder (e.g. cloud-init.sh). Absolute paths are also accepted. |
kind | string | No | vm (default; built) or cluster (k8s; reserved). |
deploy_user | object | No | Deploy user provisioned at VM-init time. See Deploy user. |
firewall | object | No | Declarative 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:
- SMALL
- MEDIUM
- LARGE
- XLARGE
Supported images
| Value | Description |
|---|---|
ubuntu-24.04 | Ubuntu 24.04 LTS |
ubuntu-22.04 | Ubuntu 22.04 LTS |
debian-12 | Debian 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
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Username 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:
| Placeholder | Description |
|---|---|
{{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
listenport on this host is opened automatically, plus:80when the host terminates TLS (nginx serves the ACME HTTP-01 challenge there). - Any rules in the
firewall.inboundblock 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.
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
| Field | Type | Required | Description |
|---|---|---|---|
proto | string | Yes | Protocol: tcp, udp, or icmp. |
port | int or string | Yes | Port number (80) or range (8000-9000). Not used when proto is icmp. |
Example
name: bridge
instance_count: 1
container: bridge
network: ingress
size: SMALL
image: ubuntu-24.04
cloud_init: cloud-init.sh
deploy_user:
name: deploy
#!/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
| Output | Description |
|---|---|
publicIP | Public IPv4 address of the VM |