TAPPaaS Build Process¶
How the artifacts that make up a TAPPaaS deployment are produced: the Proxmox install media, the prebuilt VM images (NixOS template, OPNsense firewall), and — in a separate repo — the documentation site.
Two facts shape everything below:
- Codeberg (
codeberg.org/TAPPaaS/TAPPaaS) is the source of truth for code, issues and PRs. See docs/codeberg-migration.md. - GitHub (
github.com/TAPPaaS/TAPPaaS) is a mirror of the Codeberg source — it holds no independent history. The one thing that runs there is the image build CI (GitHub Actions), whose output is hosted as GitHub Release assets. That lives on the mirror only because open-source repos get free Actions minutes and free Release bandwidth for the multi-GB images; nothing else depends on GitHub.
flowchart LR
subgraph Codeberg["Codeberg — source of truth"]
SRC[TAPPaaS source repo]
end
subgraph GH["GitHub Actions — image CI + Release hosting"]
NIX[NixOS template image]
OPN[OPNsense firewall image]
end
subgraph Docs["Codeberg — Documentation repo"]
SITE[MkDocs site → tappaas.org]
end
OP([Operator]) -->|make-install-media.sh| USB[Proxmox install USB]
SRC -->|push tag| NIX
SRC -->|push tag| OPN
SRC -->|WS0 source-sync| SITE
USB --> PVE[Proxmox node]
NIX -->|Release asset| PVE
OPN -->|Release asset| PVE 1. Proxmox install media¶
There is no custom Proxmox host image. TAPPaaS installs stock Proxmox VE and layers configuration on top via the cluster foundation module. The only build step is turning the stock ISO into an unattended installer stick.
- Script: src/foundation/cluster/make-install-media.sh
- Tech: Proxmox's own
proxmox-auto-install-assistant(validate-answer+prepare-iso --fetch-from iso), driven by a generatedanswer.toml(four operator answers: email, locale, root password, boot disk). On macOS the Linux-only assistant is run inside adebian:trixieDocker container; on Linux the.debis fetched fromdownload.proxmox.com.xorriso/cpioinject an ask-at-install boot-disk prompt into the installer initrd;ddwrites the USB. - Runner: none — this is a manual operator script, run anywhere (there is no PVE yet on the first node; chicken-and-egg).
- Result: a bootable USB stick. Not published anywhere.
See docs/design/node-provisioning.md (N2) and the cluster module (src/foundation/cluster/).
2. NixOS template image¶
The prebuilt, pre-configured NixOS VM template that every foundation/app VM is cloned from.
- Source: src/foundation/templates/flake.nix — a Nix flake pinning
nixpkgstonixos-25.11, baking in tappaas-common.nix (the hardware-agnostic TAPPaaS baseline; nohardware-configuration.nix— the image format module supplies disk/fs/EFI). Output:packages.x86_64-linux.image, an EFI qcow2 (nix build .#image, i.e.nixos-rebuild build-image --image-variant qemu-efi). - Runner / CI: GitHub Actions, .github/workflows/build-nixos-template-image.yml, on a hosted
ubuntu-latest. Nix is installed viaDeterminateSystems/nix-installer-action; the image is built withnix build, compressed withzstd -19 --long=27, and published withsoftprops/action-gh-release. - Trigger: push a tag
nixos-template-v<version>(or manualworkflow_dispatch). - Result: a GitHub Release asset with the stable, unversioned name
tappaas-nixos.qcow2.zst— the release tag carries the version. The consumer pointer is tappaas-nixos.json (imageLocation→releases/download/nixos-template-v<version>/).
flowchart LR
TAG[git tag nixos-template-vX] --> GHA[GitHub Actions ubuntu-latest]
GHA -->|nix build .#image| QCOW[EFI qcow2]
QCOW -->|zstd -19| ZST[tappaas-nixos.qcow2.zst]
ZST -->|action-gh-release| REL[(GitHub Release)]
REL -->|Create-TAPPaaS-VM.sh: curl + zstd -d| NODE[Proxmox node]
NODE -->|qm importdisk → tanka1| DISK[imported disk]
DISK -->|qm template vmid 8080| TPL[VM template] Consumption happens on the first Proxmox node during foundation install: install-platform.sh (Phase A build_template) calls Create-TAPPaaS-VM.sh, which curls the asset, zstd -ds it, qm importdisks it into the storage pool (tanka1), and qm templates VMID 8080 into a reusable template.
3. OPNsense firewall image¶
The preconfigured firewall image (issue #231) — the OPNsense analog of the NixOS template.
- Source: a base installed OPNsense qcow2 (pinned upstream:
maurice-w/opnsense-vm-images) with TAPPaaS management config injected into/conf/config.xmlfrom src/foundation/firewall/firewall-config.xml.template. It boots fully configured (LAN10.0.0.1, DNS/DHCP, hostnamefirewall, API). - Runner / CI: GitHub Actions, .github/workflows/build-opnsense-image.yml. The config injection runs inside a FreeBSD VM (
vmactions/freebsd-vm) because Linux cannot write UFS2;zstd-compressed and Release-published like the NixOS image. - Trigger: push a tag
opnsense-firewall-v<version>(or manual). - Result: GitHub Release asset
tappaas-firewall.qcow2.zst. - Secrets model — bootstrap-then-rotate: the public image bakes a well-known bootstrap API key + root password; on first deploy
config-firewall.shrotates both to unique values over the API and deletes the bootstrap key. Never leave a deployed firewall on bootstrap creds.
4. Documentation build¶
The documentation site is built in a separate repo and published to Codeberg Pages at tappaas.org. It is MkDocs (Material) built by Woodpecker CI; a source-sync step copies allow-listed README.md / INSTALL.md / DESIGN.md files out of this repo into the site at build time, so upstream docs stay canonical here and are never hand-copied.
The full documentation build — the sync mechanism, the CI pipeline and the Pages hosting — is documented in the Documentation repo: