# Deployment designs

> The architectures you can build with PowerCloud, from storage-only to multi-site clusters on confidential VMs, what each protects against, how it unlocks, and how to choose between them.

This page describes six ways to deploy PowerCloud and when to choose each. Every design rests on one split: trusted machines hold keys and run apps, and everything else (buckets, plain cloud VMs, relays) only ever holds ciphertext. What changes between designs is how many trusted machines you have, where they are, and how many independent providers hold your encrypted data.

## The building blocks

Every design is assembled from the same pieces. For definitions of node, cluster and repository, see [Concepts](https://powercloud.unidatum.com/docs/overview.md).

- **Trusted node.** A PowerCloud node on hardware you own (a home server, a machine in a colocation rack) or on a confidential VM that passed attestation. It holds keys and runs apps.
- **Untrusted location.** An S3-compatible bucket (AWS S3, Cloudflare R2, Backblaze B2, Wasabi, RustFS and similar), a local directory, or a plain cloud VM. It stores and relays ciphertext only. A provider can delete or corrupt its copy, but it cannot read it, and authentication catches any change.
- **Storage locations.** The cluster's encrypted store is written to every location in `--repo`: directories, `s3://` buckets, `node://NAME` replicas on a node's encrypted disk, or an erasure-coded group `ec:K:LOC;LOC;...`. A write succeeds once the write quorum accepts it (by default a majority of the locations). See [Storage](https://powercloud.unidatum.com/docs/storage.md).
- **Consensus.** Three or more nodes form a Raft group (`pcctl consensus init`) that keeps state they agree on. It is needed for exclusive apps, app volumes, the S3 gateway and rollback protection. Two voters tolerate no loss, so failover needs three.
- **Unlock.** A node's state volume is locked after every reboot. It is opened by an admin (`pcctl node unlock`), by the node's TPM (`pcctl node tpm NAME on`), by shares held by its peers (`pcctl node peer-unlock NAME on`), or, on an AMD SEV-SNP VM, by an admin unlock that first verifies the chip's attestation. See [Unlocking nodes after a reboot](https://powercloud.unidatum.com/docs/unlock.md).
- **Mesh and relays.** Nodes talk over WireGuard (UDP 51820). Nodes behind NAT find each other through a public node, and nodes behind symmetric NAT relay through one.

Throughout, `pcctl --repo LOC,...` names the storage locations. You can set them once in `POWERCLOUD_REPO` instead of repeating `--repo`, and once a node serves the storage (`pcctl node storage`), `pcctl --via NAME` replaces `--repo` for everyday commands.

## 1. One home server, encrypted copies in cloud buckets

```mermaid
%% alt: Design 1: an admin laptop manages the trusted home server over mutual TLS, and the home server writes only ciphertext to buckets on two providers.
flowchart TB
  admin["Admin laptop<br/>pcctl, passphrase"]
  subgraph home["Home: trusted"]
    h1["home-1<br/>node://home-1 replica, apps"]
  end
  subgraph untrusted["Untrusted providers"]
    direction LR
    A[("Bucket on provider A")]
    B[("Bucket on provider B")]
  end
  admin -->|"mutual TLS, TCP 7443"| h1
  h1 -->|ciphertext| A
  h1 -->|ciphertext| B
```

**Who it suits.** A person or a small household that wants its own server at home, with off-site copies that no single cloud can read or take away.

**How it is set up.** Create the repository on the buckets, enroll the home server, add its disk as a location and copy everything onto it, then let it serve the storage:

```sh
export POWERCLOUD_REPO='s3://pc-home?endpoint=https://ACCOUNT_ID.r2.cloudflarestorage.com&region=auto&creds=r2,s3://pc-home?endpoint=https://s3.REGION.backblazeb2.com&region=REGION&creds=b2'
pcctl init
# enroll home-1 (see /docs/enroll), then:
export POWERCLOUD_REPO="node://home-1,$POWERCLOUD_REPO"
pcctl repair
pcctl node storage home-1
pcctl --via home-1 put photos/cat.jpg ./cat.jpg
```

With three locations, a write needs two of them, so it still succeeds while one bucket provider is down. The home server's scheduled sync fills in what that provider missed.

**What it protects against.** A bucket provider reading the data, tampering with it, cutting off the account or going out of business. A stolen disk from the home server (the state volume is encrypted, with an integrity tag on every sector).

**What it doesn't.** The home server is a single point of failure for apps: when it is down, nothing runs. One node can't form consensus, so there is no rollback protection (pcctl warns), no exclusive apps, no app volumes and no S3 gateway. A burglar who takes the running machine with TPM unlock on gets a running, enrolled node, protected only by its API and lockdown.

**Unlock.** TPM unlock, if the machine has a TPM 2.0 and boots with Secure Boot under your own key; otherwise unlock by hand after each reboot. Peer unlock needs peers, so it doesn't apply.

**Cost class.** Lowest: your own hardware and electricity, plus bucket storage. See [Costs and sizing](https://powercloud.unidatum.com/docs/sizing.md).

**What to watch.** Restoring from the buckets after losing the server sends every byte out of those providers, so check their egress fees. Run `pcctl check` now and then; it compares every location.

## 2. Home, colo and buckets across several providers

```mermaid
%% alt: Design 2: trusted nodes at home and in a colocation rack, joined by WireGuard, both holding replicas, with ciphertext on buckets at three providers.
flowchart TB
  subgraph trusted["Trusted: two sites"]
    direction LR
    h1["home-1<br/>at home"]
    c1["colo-1<br/>in a colo rack"]
    h1 <-->|WireGuard| c1
  end
  subgraph untrusted["Untrusted: full replicas, or one ec:4: group with the nodes"]
    direction LR
    A[("Provider A")]
    B[("Provider B")]
    C[("Provider C")]
    A ~~~ B ~~~ C
  end
  trusted -->|"ciphertext only"| untrusted
```

**Who it suits.** Someone who wants no single provider, building or internet connection to be able to take the data away, and who can place a second machine somewhere else: a colo rack, an office, a relative's house.

**How it is set up.** Both machines are enrolled and hold `node://` replicas, next to buckets on several providers. Full replicas cost the data times the number of locations. Erasure coding spreads each blob as data and parity shards, so `ec:4:` over six locations (four buckets and the two nodes) serves reads with any two lost, at 1.5 times the data (writes need five of the six). The nodes' disks can only join once the nodes are enrolled, so start from the repository you enrolled them with, add the erasure-coded group next to it, copy everything across, then use the group on its own:

```sh
EC='ec:4:s3://...&creds=r2;s3://...&creds=b2;s3://...&creds=wasabi;s3://...;node://home-1;node://colo-1'
pcctl --repo "OLD_LOC,OLD_LOC,$EC" repair
pcctl --repo "$EC" check
```

**What it protects against.** Everything design 1 does, plus losing a whole site or a whole provider. Either node can serve the storage, so admins keep working with one site down.

**What it doesn't.** Two nodes can't form a consensus group that survives losing either one, so this design still lacks failover for exclusive apps and dependable rollback protection. Add a third node (design 4's reasoning applies) to get them. Ordinary cluster apps do fail over between the two nodes.

**Unlock.** TPM unlock on both, or peer unlock: each node holds the other's share, so a node that reboots alone unlocks itself. If both reboot together (a shared power cut is unlikely across two sites, but possible), neither can answer the other, and one needs an admin unlock first.

**Cost class.** Low to moderate: two machines, a colo fee, and storage on several providers. See [Costs and sizing](https://powercloud.unidatum.com/docs/sizing.md).

**What to watch.** Each write leaves its site once per remote location, and `pcctl repair` after adding a location copies everything, so egress and request charges add up. GC refuses to run while any location is unreachable.

## 3. All cloud: confidential VMs plus ciphertext-only storage

```mermaid
%% alt: Design 3: three attested AMD SEV-SNP VMs on three clouds form the trusted consensus group, while buckets, plain VMs and relays on any cloud hold only ciphertext.
flowchart TB
  admin["Admin laptop<br/>pcctl"]
  subgraph trusted["Trusted: attested AMD SEV-SNP VMs, three voters"]
    direction LR
    v1["cvm-1<br/>cloud X"]
    v2["cvm-2<br/>cloud Y"]
    v3["cvm-3<br/>cloud Z"]
    v1 ~~~ v2 ~~~ v3
  end
  subgraph untrusted["Untrusted: ciphertext only"]
    direction LR
    b[("Buckets on X, Y, Z")]
    p["Plain VMs, relays"]
    b ~~~ p
  end
  admin -->|"verifies attestation,<br/>then sends volume key"| trusted
  trusted -->|ciphertext| untrusted
```

**Who it suits.** Teams with no hardware of their own who still want no cloud to be able to read their data or shut them down.

**Why confidential VMs.** On someone else's VM the host can read the VM's memory, and with it the keys. On an AMD SEV-SNP VM the CPU encrypts that memory and signs a report saying so. pcctl verifies the report (signed by the chip's endorsement key, chained to AMD's root, debugging off, bound to the node's unlock key) before it sends a volume key, and requires it at every unlock from then on. A plain VM should never hold keys.

**What is built and what is tested.**

- AWS: images boot on x86 and Graviton. SEV-SNP attested unlock has been run on AWS on a c6a.large with SEV-SNP on. AWS offers SEV-SNP on shared tenancy only in US East (Ohio) and Europe (Ireland).
- Google Cloud and Azure: each release carries their images, with upload scripts, but the images have not been booted on either cloud yet. The Google Cloud image is created SEV-SNP capable on x86. The Azure script creates Trusted Launch images, not confidential VM images.
- Intel TDX and Arm CCA attestation are not built. Only AMD SEV-SNP reports are verified.

So today a three-provider confidential cluster is a design you can plan for, but only its AWS part has been run.

**What it protects against.** Any one cloud reading memory or storage (on attested nodes), and any one cloud pulling the plug: two trusted nodes and a consensus majority remain.

**What it doesn't.** The attestation report measures the cloud's own firmware, not PowerCloud's kernel, which is vouched for by Secure Boot instead; pcctl shows the measurement but doesn't pin it. AMD's revocation lists aren't checked. A node that unlocks itself with its TPM attests to no one (check it with `pcctl node attest NAME`). Most cloud VMs don't offer virtualisation to the guest, so apps with `--vm` won't run there.

**Unlock.** Admin unlock with attestation, after each reboot, keeps the guarantee whole. TPM unlock and peer unlock are available, but neither checks attestation.

**Cost class.** Highest: about $210 a month for three small SEV-SNP VMs on three clouds, compute only, at prices checked on 2026-10-24. See [Costs and sizing](https://powercloud.unidatum.com/docs/sizing.md).

**What to watch.** Replicating between clouds is egress on every write. On AWS, a VM that restarts gets a new public IP unless it has an Elastic IP; run `pcctl node addr NAME NEW_IP` after.

## 4. A small business office cluster

```mermaid
%% alt: Design 4: three office nodes form a consensus group running cluster apps and the S3 gateway for staff machines, and send ciphertext to off-site buckets.
flowchart TB
  staff["Staff machines,<br/>backup software"]
  subgraph office["Office LAN: trusted, three voters"]
    direction LR
    o1["office-1"]
    o2["office-2"]
    o3["office-3"]
    o1 ~~~ o2 ~~~ o3
  end
  subgraph offsite["Off-site: untrusted"]
    b[("Buckets on 2+ providers<br/>or an ec:K: group")]
  end
  staff -->|"apps, S3 on TCP 9443"| office
  office -->|ciphertext| offsite
```

**Who it suits.** An office that wants its own servers for files, backups and internal apps, with one machine allowed to fail.

**How it is set up.** Three nodes, consensus founded on all three, the storage served by the nodes, cluster apps placed by the nodes, and the S3 gateway for tools that already speak S3:

```sh
pcctl --repo ... consensus init --nodes office-1,office-2,office-3
pcctl --repo ... node storage office-1     # likewise office-2 and office-3
pcctl --repo ... cluster app deploy --exclusive --volume --mem 512M --port 8080:8080 wiki ./wiki
pcctl --repo ... cluster app deploy --replicas 2 --image nginx:alpine --port 80:80 site
pcctl --repo ... s3 enable --name s3.office.example office-1 office-2 office-3
pcctl --repo ... s3 key add --bucket backups backup-server
```

An exclusive app runs on at most one node and moves when its node goes away; with `--volume` its data directory follows it. Clients use whichever gateway node is up, or a DNS name for all three. See [Cluster apps](https://powercloud.unidatum.com/docs/cluster-apps.md) and [S3 gateway](https://powercloud.unidatum.com/docs/s3-gateway.md).

**What it protects against.** One node failing (apps restart elsewhere after about a minute, consensus keeps a majority), a provider holding the off-site copies, a stolen disk, a provider serving an old version of an object (rollback protection).

**What it doesn't.** One building: fire, theft of all three machines or a long power cut takes everything local down; the off-site locations hold the data, not the running apps. An exclusive app's volume is saved every 30 seconds, so a node that dies loses up to 30 seconds of its changes, and databases should keep their state in `--storage` or checkpoint it. Two nodes lost at once stop consensus, and with it exclusive apps and the gateway's key changes.

**Unlock.** Peer unlock fits well: a node that reboots alone unlocks itself from its peers' shares, and one carried out of the office stays locked. The default threshold is a majority of a node's peers, which is both of the other two in a three-node cluster; `--threshold 1` lets a node unlock with one peer up. After a power cut that reboots all three, the locked nodes can't answer each other, so unlock enough of them by hand for the rest to follow, or combine it with TPM unlock.

**Cost class.** Moderate: three small machines you own, plus off-site storage. See [Costs and sizing](https://powercloud.unidatum.com/docs/sizing.md).

**What to watch.** Placement counts apps' memory limits, not their use, so an app that fits nowhere runs nowhere and `pcctl status` says so. Leave each node room for the apps of a failed peer.

## 5. Edge and behind-NAT nodes with relays

```mermaid
%% alt: Design 5: two nodes behind NAT talk directly when their NATs allow it; behind symmetric NAT they relay through a public node, which sees only WireGuard ciphertext.
flowchart TB
  subgraph nat1["Behind a home router"]
    e1["edge-1"]
  end
  relay["relay-1<br/>public node"]
  subgraph nat2["Behind CGNAT, 4G or a firewall"]
    e2["edge-2"]
  end
  e1 <-->|"relayed: symmetric NAT"| relay
  relay <-->|"WireGuard ciphertext"| e2
  e1 <-.->|"direct: most NATs"| e2
```

**Who it suits.** Machines in places with no public address: a home behind a router, a shop or site on a mobile connection behind carrier-grade NAT, a branch office behind a corporate firewall.

**How it works.** A node behind NAT has no endpoint, but connects outwards. A public node learns its NAT's address from its packets and tells other nodes, so two nodes behind ordinary NATs connect directly. When their NATs pick a new port for every destination (symmetric NAT), they relay through a public node instead, which sees only WireGuard ciphertext. Relayed pairs try a direct path again every 10 minutes, backing off up to about 10 hours. A home node with a changing address can use a DNS name as its endpoint: `pcctl mesh endpoint home-1 home.example.net:51820`.

**What it protects against.** The same as the designs the edge nodes belong to. The relay can drop traffic but can't read or change it.

**What it doesn't.** Every pair of NATed nodes needs a third node both can reach. Relayed traffic costs the relay's bandwidth and a detour. The mesh carries traffic between nodes; pcctl reaches each node's API (TCP 7443) at the address set with `pcctl node addr`, so admins still need a network path to a NATed node.

**Unlock.** A locked node asks its peer-unlock holders outside the mesh, at their API addresses, so holders behind NAT can't be reached from elsewhere. Make public nodes the holders, set a reachable address with `pcctl mesh api NAME HOST:PORT`, or use TPM unlock.

**Cost class.** Adds one public node (it can be small) to whatever design the edge nodes join. Its outbound traffic is billed by its provider. See [Costs and sizing](https://powercloud.unidatum.com/docs/sizing.md).

**What to watch.** If the first peer to report a missing node is on that node's LAN, it reports a LAN address that nodes elsewhere can't use, until that peer's session lapses.

## 6. Storage only: pcctl and buckets, no nodes

```mermaid
%% alt: Design 6: pcctl on the admin laptop encrypts and decrypts locally and writes ciphertext to a local directory and two buckets, with no nodes at all.
flowchart TB
  subgraph laptop["Admin laptop: trusted"]
    pc["pcctl, passphrase<br/>encrypts and decrypts locally"]
  end
  subgraph untrusted["Storage locations: ciphertext only"]
    direction LR
    d[("/srv/pc<br/>local disk")]
    A[("Bucket on<br/>provider A")]
    B[("Bucket on<br/>provider B")]
  end
  pc -->|ciphertext| d
  pc -->|ciphertext| A
  pc -->|ciphertext| B
```

**Who it suits.** Encrypted, multi-provider backup and file storage with nothing to run: no servers, no images.

**How it is set up.**

```sh
pcctl --repo /srv/pc,'s3://pc-b?endpoint=https://s3.REGION.wasabisys.com&region=REGION&creds=wasabi','s3://pc-c?region=us-east-2' init
pcctl --repo ... put docs/report.pdf ./report.pdf
pcctl --repo ... check
pcctl --repo ... gc
```

Erasure coding works here too. Every machine that uses the repository needs every location's credentials, since no node holds them.

**What it protects against.** Providers reading, altering or losing the data.

**What it doesn't.** With no nodes there is no consensus, so no rollback protection (pcctl warns on every command): a provider can serve an older authentic version of an object. Two admins writing the same name at once: the later write wins. There are no apps, no `--via`, no scheduled sync and no S3 gateway; run `pcctl repair` yourself after a provider was down.

**Unlock.** Only the passphrase or recovery key; there are no node volumes.

**Cost class.** Storage and egress only. See [Costs and sizing](https://powercloud.unidatum.com/docs/sizing.md).

**What to watch.** `check --verify` downloads every chunk copy, which is egress on every provider. Start here and add nodes later: enrolling a node and adding `node://NAME` to `--repo` turns this into design 1.

## Choosing a design

Start from what you need and follow the questions; the table below says the same in short. Design 5 is not a branch: add it to whichever design you reach when some of its nodes have no public address.

```mermaid
%% alt: A decision flowchart leading from what you need to one of designs 1, 2, 3, 4 or 6; design 5 is added to any of them for nodes with no public address.
flowchart TB
  q1{"Run apps<br/>or a server?"}
  q2{"Own hardware?"}
  q3{"Failover, S3 gateway<br/>or rollback protection?"}
  q4{"No single site or provider<br/>can take data away?"}
  d6["6. Storage only"]
  d3["3. Confidential VMs"]
  d4["4. Three-node cluster"]
  d2["2. Home, colo and buckets"]
  d1["1. Home plus buckets"]
  q1 -->|no| d6
  q1 -->|yes| q2
  q2 -->|no| d3
  q2 -->|yes| q3
  q3 -->|yes| d4
  q3 -->|no| q4
  q4 -->|yes| d2
  q4 -->|no| d1
```

| You need | Design |
| --- | --- |
| Encrypted off-site backup, nothing to run | 6. Storage only |
| A home server with off-site copies | 1. Home plus buckets |
| No single site or provider can take data away | 2. Home, colo and buckets |
| No hardware of your own | 3. Confidential VMs (AWS tested today) |
| Apps that survive a machine failing, an S3 endpoint for existing tools | 4. Three-node cluster |
| Nodes with no public address | 5. Edge nodes, plus one public node |
| Rollback protection | Any design with three nodes and consensus |

The designs combine: an office cluster (4) with a confidential VM off-site (3) and an edge node at a branch (5) is one cluster.

## Trade-offs

**Replication or erasure coding.** Replicas cost the data times the number of locations, and any one copy serves a read. Erasure coding with k data shards over n locations costs n/k times the data and survives losing n−k locations, but a read needs k providers to answer and a write needs k+1. Use replicas for a few locations or small data; erasure coding when you spread over many providers.

**Write quorum.** The default is a majority of locations (`--quorum` changes it). A higher quorum means a write is on more locations before it is acknowledged; a lower one lets writes go on with more locations down, leaving more for sync or `pcctl repair` to fill in.

**One node or three.** One node is simplest and cheapest but has no failover and no consensus. Two nodes give a second site but no consensus that survives a loss: two voters need both. Three or more give failover for exclusive apps, the S3 gateway and rollback protection, with one node allowed to fail.

**Trusted or untrusted placement.** Put a node that holds keys only on hardware you control or on an attested SEV-SNP VM. A plain VM enrolled as a node never gets the cluster keys, but it is still a full member: it votes in consensus, may relay, and is eligible for cluster apps unless you name the allowed nodes with `--nodes`. Buckets are the simplest untrusted placement.

**TPM, peer or manual unlock.**

| Method | Unlocks by itself | Stays locked when | Watch |
| --- | --- | --- | --- |
| Manual (`pcctl node unlock`) | Never | Always, until an admin unlocks | Someone must act after every reboot |
| TPM (`node tpm NAME on`) | Same machine, Secure Boot on with your key | Disk moved, Secure Boot off or keys changed | A stolen running machine is a running node |
| Peer (`node peer-unlock NAME on`) | Enough peers reachable | Taken away from its peers, or removed | A cluster-wide reboot needs a manual start |
| Attested (SEV-SNP, manual) | Never | Not on a verified SEV-SNP VM | TPM and peer unlock skip attestation |

## Good to know

- Google Cloud and Azure images are built and have upload scripts but have not been booted on those clouds yet. Intel TDX and Arm CCA attestation are planned; see [Roadmap](https://powercloud.unidatum.com/roadmap).
- Losing every passphrase and the recovery key loses the data in every design. There is no backdoor.
- Moving between designs is adding and removing locations and nodes: add a location to `--repo`, run `pcctl repair`, then drop the old one.
- For the security model behind these designs, see [Security model](https://powercloud.unidatum.com/security).
