# Authoring templates bootapi ships an embedded default set and lets you override or extend it. ## Where templates live - **Embedded defaults**: `templates/kickstart/*.ks.tmpl` and `templates/ipxe/*.ipxe.tmpl`, compiled into the binary (`templates/embed.go`). - **Overrides**: any directory pointed to by `BOOTAPI_TEMPLATE_DIR`. Files there with the same base name **replace** the embedded one; new names **add** to the set. In Kubernetes this is a ConfigMap mount (see [deployment.md](deployment.md)). ## Naming - Kickstart: `.ks.tmpl` → registered as template ``. - iPXE: `.ipxe.tmpl` → registered as template ``. `` is what template selection matches against (platform slug, OS family, `provision_template`, or the configured default — see [data-model.md](data-model.md#template-selection-precedence)). Reserved iPXE names bootapi renders directly: - `boot` — the per-host boot script (`/ipxe/{mac}` for a known host). - `fallback-local`, `fallback-shell` — unknown-MAC fallbacks. ## Engine and functions Standard Go `text/template`. Available funcs: `join`, `upper`, `lower`, `default` (`{{ default "x" .Maybe }}` → `.Maybe` unless empty). The data model is in [data-model.md](data-model.md). Example network stanza (iterate interfaces, skip those without an IP, set the hostname on the primary): ```gotemplate {{- $primary := .PrimaryInterface }} {{- range .Interfaces }} {{- if .IP }} network --bootproto=static --device={{ .MAC }} --ip={{ .IP }} --netmask={{ .Netmask }}{{ if .Gateway }} --gateway={{ .Gateway }}{{ end }}{{ range $.Nameservers }} --nameserver={{ . }}{{ end }}{{ if and $primary (eq .MAC $primary.MAC) }} --hostname={{ $.FQDN }}{{ end }} --activate {{- end }} {{- end }} ``` ## What the default AlmaLinux template does (ported from Cobbler) The literal `.ks` bodies from the old Cobbler server are not in version control (they lived in `/var/lib/cobbler/{templates,snippets}` on the Cobbler host). The embedded `almalinux9.ks.tmpl` reproduces the **contract** that estate relied on: - static per-interface networking from NetBox, hostname on the primary NIC; - `rootpw --iscrypted` from the render-time hash (Cobbler's `default_password_crypted`), or `--lock` when unset; - a minimal package set + `openssh-server`, `chrony`; - a `%post` that installs the Puppet agent, points it at `puppet.query.consul` / `puppetca.query.consul`, and enables it — handing off to the existing `profiles::firstrun` Puppet bootstrap and autosign, exactly as the Cobbler kickstart did. Adjust partitioning, package sets and repos to taste; keep the puppet `%post` handoff so a freshly-installed host still checks in and converges. ## Testing a template locally ```bash BOOTAPI_NETBOX_URL=... BOOTAPI_NETBOX_TOKEN=... \ BOOTAPI_BASE_URL=http://localhost:8000 \ BOOTAPI_BOOT_BASE_URL=http://mirror/almalinux/9 \ BOOTAPI_TEMPLATE_DIR=./mytemplates ./bin/bootapi & curl -s localhost:8000/ks/web01 # rendered kickstart curl -s localhost:8000/ipxe/aa:bb:cc:00:11:22 ```