Install a node
How to run PowerCloud OS on a physical machine, a QEMU, libvirt or Proxmox VM, AWS, Google Cloud or Azure - the image files, writing and booting them, cloud upload scripts, firewall ports, supported hardware and Secure Boot.
A PowerCloud node is any x86-64 or arm64 machine, physical or virtual, that boots the PowerCloud disk image with UEFI firmware. This page covers getting the image onto a machine and booting it; once it is up, it waits to be enrolled, which Enroll a node covers. AWS is tested on real instances; Google Cloud and Azure images and upload scripts exist but have not yet been booted on those clouds.
The images
Each PowerCloud release carries, for amd64 and arm64 (VERSION is the release name, vYYYY.MM.DD):
| File | What it is | Used for |
|---|---|---|
powercloud-VERSION-ARCH.img.xz |
The bootable disk image, compressed | Physical machines, VMs, and AWS (decompressed) |
powercloud-VERSION-ARCH.gce.tar.gz |
The same disk image packed for Google Cloud | scripts/gcp-image.sh |
powercloud-VERSION-ARCH.vhd.xz |
The same disk image as a fixed VHD, compressed | scripts/azure-image.sh |
powercloud-VERSION-ARCH.efi |
The OS alone, as one EFI file | Upgrading running nodes (pcctl release publish; see Upgrades and releases) |
SHA256SUMS |
Checksums of every file | Checking downloads |
Check what you download with sha256sum -c SHA256SUMS --ignore-missing. To build the images from the PowerCloud source instead, make image writes out/powercloud-amd64.img and out/powercloud-arm64.img (and the .efi files), and make cloud-images packs them for Google Cloud and Azure. The upload scripts named on this page are in the source's scripts/ directory.
The disk image is a 1 GiB GPT disk with two partitions:
- An EFI system partition (256 MiB) holding the whole OS as one file at the firmware's default boot path: the kernel with its initramfs and a fixed command line built in. There is no boot loader and no boot menu, and nothing outside can add kernel options.
- The state partition, empty until the node is enrolled. At enrollment the node creates its encrypted state volume there. On every boot it grows the partition to fill the disk, so a disk larger than the image (or a cloud disk resized later) is used in full.
What you see when it boots
The kernel logs to the serial console and to a monitor, if one is attached. pcd prints its message to both:
PowerCloud node 262c2fdc861a9cf2 is waiting to be enrolled.
On your admin machine run:
pcctl node enroll <this-node-address> <code> <name>
enrollment code: EYWC-7XEG-DKOP-F2IR-23UA-GTJQ-5G3M-ZXTO
The node gets its network address by DHCP. Its API listens on TCP port 7443, which your admin machine must be able to reach. After enrollment, every reboot shows an unlock code instead, unless TPM or peer unlock is on (see Unlocking nodes after a reboot). A machine given a join bundle skips the code and joins by itself (see Enroll a node).
Physical machines
Decompress and write the image to the disk the machine will boot from. On Linux, with the target disk as
/dev/sdX(double-check the name: everything on it is overwritten):xz -d powercloud-VERSION-amd64.img.xz sudo dd if=powercloud-VERSION-amd64.img of=/dev/sdX bs=4M conv=fsyncBoot it with UEFI. Legacy BIOS boot is not supported; turn off CSM or legacy mode if the firmware has it. The firmware finds the OS at the default path, so no boot entry is needed.
Secure Boot. Release images are not signed for Secure Boot. On a machine with Secure Boot on (the default on most PCs), either turn it off in the firmware setup, or build images signed with your own key and enroll it (below).
Read the enrollment code from the monitor or serial console, and enroll the node from your admin machine.
Caveats when writing the disk:
- Don't use
conv=sparse. The state partition must start out zeroed, andpcdrefuses to format a state partition whose first MiB holds anything else. A sparse write leaves whatever the disk held before. ddoverwrites only the image's size (1 GiB). Erase a disk that held sensitive data first, withblkdiscardor the drive's own secure erase.- One PowerCloud disk per machine. If two disks in the same machine carry a PowerCloud state partition,
pcdstops with "several PowerCloud state partitions". Erase the one you don't want. - arm64 machines need UEFI firmware with ACPI, as Arm servers have. The arm64 image boots through UEFI and describes the machine with ACPI, not a device tree.
Virtual machines: QEMU, libvirt and Proxmox
Any KVM host works: give the VM UEFI firmware (OVMF on x86, AAVMF on arm64), the disk image as a virtio disk, and a virtio NIC. On a Debian or Ubuntu host with the ovmf package, a QEMU node with its API forwarded to the host's port 7443 looks like this:
cp /usr/share/OVMF/OVMF_VARS_4M.fd node-vars.fd
qemu-img resize -f raw powercloud-VERSION-amd64.img 8G # optional: more room for the state volume
qemu-system-x86_64 -machine q35,accel=kvm -cpu max -smp 2 -m 1G -nographic \
-drive if=pflash,format=raw,unit=0,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
-drive if=pflash,format=raw,unit=1,file=node-vars.fd \
-drive file=powercloud-VERSION-amd64.img,format=raw,if=virtio \
-netdev user,id=n0,hostfwd=tcp::7443-:7443,hostfwd=udp::51820-:51820 \
-device virtio-net-pci,netdev=n0 -device virtio-rng-pci
With -nographic, the serial console (and the enrollment code) is in the terminal. Enroll it at 127.0.0.1. The automated tests run nodes like this with 1 GiB of memory.
For a VM to join with nobody at its console, give it a join bundle as a QEMU fw_cfg file:
pcctl --repo LOC,... node provision vm-1 vm-1.join
# add to the qemu command line:
-fw_cfg name=opt/powercloud/join,file=vm-1.join
libvirt (a fwcfg sysinfo entry) and Proxmox (the VM's args option) can pass the same fw_cfg file; the plain QEMU path is the one covered by automated tests. See Enroll a node for what the node does with the bundle.
The same image runs on VMware (vmxnet3 network, PVSCSI or SATA disk) with UEFI firmware.
AWS
scripts/aws-image.sh turns a disk image into an AMI in about a minute. It writes the image straight into an EBS snapshot (skipping all-zero blocks, so no S3 bucket or import role is needed) and registers a UEFI AMI with ENA networking and NitroTPM. It uses the AWS CLI's usual credentials and region, and prints the AMI ID.
xz -d powercloud-VERSION-arm64.img.xz
scripts/aws-image.sh arm64 powercloud-VERSION-arm64.img # amd64 for x86 instances
Launch it on any Nitro instance type, x86 (t3, m6i, ...) or Graviton (t4g, m7g, ...), with a join bundle as user data so it joins unattended:
pcctl --repo LOC,... node provision node-1 node-1.join
aws ec2 run-instances --image-id AMI_ID --instance-type t4g.micro \
--metadata-options HttpTokens=required --user-data file://node-1.join ...
pcctl --repo LOC,... node addr node-1 PUBLIC_IP
- Security group. Open TCP 7443 (the node API) to your admin machines only, and UDP 51820 (WireGuard) to the internet. A public node that relays for nodes behind strict NATs also needs UDP 51821-51920.
- Without user data, the node waits for console enrollment, and
aws ec2 get-console-output --instance-id ID --latestshows the code. - New public IP after a stop and start. Run
pcctl --repo LOC,... node addr NAME NEW_IP, which also updates the node's mesh endpoint for its peers. An Elastic IP avoids this. - Secure Boot. With
POWERCLOUD_SB_CERTset to the certificate your image was signed with, the AMI turns Secure Boot on with your key as its only trusted key, so instances boot only images you signed. Thenpcctl --repo LOC,... node tpm NAME onlets the node unlock itself with its NitroTPM after a stop and start. This needs the PowerCloud source and a Go toolchain on the machine running the script. - Confidential VMs. On AMD SEV-SNP instances (c6a, m6a, r6a, offered by AWS on shared tenancy in US East (Ohio) and Europe (Ireland)), pcctl verifies the CPU's attestation before it unlocks the node; see Unlocking nodes after a reboot.
Limits: older Xen instance types (t2, m4, ...) are not supported. NitroTPM is AWS's virtual TPM, so TPM unlock protects against a stolen EBS snapshot, not against AWS itself.
Google Cloud (not yet tried)
The Google Cloud image and script are built to Google's documented import format and CLI, but have not yet been booted on Google Cloud. Expect to be among the first to try them.
scripts/gcp-image.sh copies the image into a Cloud Storage bucket you own, creates a Compute Engine image from it (UEFI, gVNIC, and SEV-SNP capable on x86), deletes the uploaded file and prints the image name. It uses gcloud's usual account and project, and takes either the release's .gce.tar.gz or a disk image (which needs the PowerCloud source and Go to pack).
GCP_BUCKET=my-bucket scripts/gcp-image.sh arm64 powercloud-VERSION-arm64.gce.tar.gz
gcloud compute instances create node-1 --image IMAGE --machine-type t2a-standard-1 \
--network-interface nic-type=GVNIC --shielded-vtpm \
--metadata-from-file user-data=node-1.join ...
Launch it as a Shielded VM (which has a vTPM), x86 (n2d, c3, ...) or Arm (t2a, c4a). Allow TCP 7443 from your admin machines and UDP 51820 from anywhere in the VPC firewall. With POWERCLOUD_SB_CERT, the image carries your certificate as its Secure Boot keys, and --shielded-secure-boot boots only images you signed; without it, leave Secure Boot off, since Google's default keys don't trust PowerCloud images.
Azure (not yet tried)
The Azure image and script are built to Azure's documented import format and CLI, but have not yet been booted on Azure.
AZURE_RESOURCE_GROUP=GROUP scripts/azure-image.sh uploads the VHD into a managed disk with azcopy and makes it a version of the image powercloud-ARCH in an Azure Compute Gallery named powercloud in that group, creating both if missing. It uses the Azure CLI's usual account and subscription, takes the release's .vhd.xz or a disk image, and prints the image version's ID.
AZURE_RESOURCE_GROUP=my-group scripts/azure-image.sh amd64 powercloud-VERSION-amd64.vhd.xz
az vm create -g my-group -n node-1 --image IMAGE_VERSION_ID --specialized --size Standard_D2as_v6 \
--security-type TrustedLaunch --enable-secure-boot false --enable-vtpm true \
--user-data node-1.join --nsg-rule NONE ...
The images are Generation 2 (UEFI) and specialized, so Azure provisions nothing in them; the node joins from its user data. On x86 they are Trusted Launch, which gives instances a vTPM. Arm sizes (Standard_D2ps_v6, ...) take the same command without the three security options. Secure Boot stays off, because Azure's default keys don't trust PowerCloud images. --nsg-rule NONE opens nothing, so add network security group rules for TCP 7443 from your admin machines and UDP 51820.
Supported hardware
Every driver is built into the kernel, which loads no modules. Network cards whose drivers need a separate firmware file are not supported.
| Where | Network | Disk |
|---|---|---|
| KVM: Proxmox, libvirt, Hetzner, DigitalOcean, Vultr, ... | virtio-net | virtio-blk, virtio-scsi |
| AWS (Nitro instances, x86 and Graviton) | ENA | NVMe |
| Google Cloud (x86 and Arm) | gVNIC, virtio-net | NVMe, virtio-scsi |
| Azure (x86 and Cobalt) | Hyper-V netvsc, MANA | Hyper-V storvsc, NVMe |
| VMware | vmxnet3 | PVSCSI (amd64), SATA |
| Physical servers | Intel e1000e, igb, igc, ixgbe, i40e, ice; Mellanox ConnectX-4 and later; Broadcom NetXtreme-C/E; Realtek r8169 | NVMe, SATA (AHCI), Broadcom MegaRAID, SAS3 HBAs |
What has actually run: the drivers QEMU can emulate (virtio, e1000e, vmxnet3, SATA, MegaRAID, NVMe) are checked in automated tests, with one node's disk moved between controllers; ENA and NVMe have run on real AWS instances, x86 and Graviton. gVNIC, Hyper-V and MANA are built in but have not run yet. Physical machines are expected to work with the drivers above but are not part of the automated tests.
Secure Boot with your own key
Because the whole OS is one EFI file, one signature covers everything up to the services. With Secure Boot on and your key enrolled, the firmware boots only images you signed: not a modified image, and not one a cloud provider swaps in.
Create a key once, and keep
db.keyoffline:scripts/sb-keygen.sh ~/powercloud-sbIt writes
db.key(private),db.crt(for signing) anddb.cer(for enrolling in firmware).Build images signed with it, from the PowerCloud source:
POWERCLOUD_SB_KEY=~/powercloud-sb/db.key POWERCLOUD_SB_CERT=~/powercloud-sb/db.crt make imageEnroll the certificate: on a physical machine, in the firmware setup (Secure Boot custom mode, add
db.certodb); on AWS and Google Cloud, by passingPOWERCLOUD_SB_CERTto the upload script. Enrolling on real hardware has not been tested yet.
pcctl node info NAME shows boot UEFI, Secure Boot on once it works. Sign every later image with the same key, or the firmware refuses it (an upgrade then rolls back by itself). Secure Boot is also what TPM unlock builds on; see Unlocking nodes after a reboot.
Next steps
- Enroll a node: enroll the node from its console, or join it unattended.
- Troubleshooting: a node that doesn't boot, can't be reached or won't format its disk.