> For the complete documentation index, see [llms.txt](https://docs.ionos.com/cloud/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ionos.com/cloud/tutorials/compute-services/confidential-computing-vm/deploy-confidential-vm-with-snpguard.md).

# Deploy a Confidential VM with SNPGuard

## Overview

[<mark style="color:blue;">SNPGuard</mark>](https://github.com/ionos-cloud/snp-guard) is an open-source attestation toolchain built by <code class="expression">space.vars.ionos\_cloud</code> for [<mark style="color:blue;">AMD SEV-SNP</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#amd-secure-encrypted-virtualization-with-secure-nested-paging-amd-sev-snp) Confidential VM. It solves three technical challenges that every Confidential VM deployment faces:

* Calculating a precise launch measurement
* Delivering the [<mark style="color:blue;">Volume Master Key (VMK)</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#volume-master-key-vmk) only after the VM proves its integrity
* Bootstrapping a trusted agent inside the early boot environment before the Operating System (OS) starts.

In this tutorial, you will work through the complete lifecycle:

* Deploying the SNPGuard attestation server
* Converting a standard Linux cloud image into a [<mark style="color:blue;">LUKS2-encrypted</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#linux-unified-key-setup-luks) confidential image
* Registering the image measurement with the server
* Embedding the signed [<mark style="color:blue;">launch\_artifacts</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#launch_artifacts)
* Uploading an image to <code class="expression">space.vars.ionos\_cloud</code>
* Creating the Confidential VM
* Confirming that attestation succeeds

The following table lists the <code class="expression">space.vars.ionos\_cloud</code> components used in this tutorial:

| **Component**       | **Description**                                        | **Configuration in this tutorial**                 |
| ------------------- | ------------------------------------------------------ | -------------------------------------------------- |
| **Confidential VM** | AMD SEV-SNP hardware-encrypted VMs                     | Frankfurt-East `de/fra/2` region                   |
| **Compute Engine**  | VM creation and lifecycle management                   | `ENTERPRISE` server type, AMD SEV-SNP `CPU` family |
| **Block Storage**   | Startup volume provisioned from the confidential image | `Balanced` or `Performance` volume                 |
| **FTP Storage**     | Upload endpoint for confidential images                | `/confidential-images/` folder                     |

## Target audience

This tutorial targets:

* **DevOps and platform engineers** who need to deploy hardware-attested workloads on <code class="expression">space.vars.ionos\_cloud</code> and want full control over the attestation service and key management.
* **Security engineers** evaluating AMD SEV-SNP attestation for regulated workloads, zero-trust architectures, or multi-party data processing scenarios.
* **Developers** building applications that run inside a Confidential VM and need to understand how the boot and attestation flow works end to end.

## What you will learn

By the end of this tutorial, you will have:

* **Deployed the SNPGuard attestation server:** Configured [<mark style="color:blue;">Transport Layer Security (TLS)</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#transport-layer-security-tls), retrieved an API token, and verified the server is active and reachable.
* **Built a confidential image:** Converted a stock Debian or Ubuntu cloud image into a `LUKS2`-encrypted confidential image with the SNPGuard attestation agent injected into the `initrd`.
* **Registered a measurement policy:** Uploaded the `SHA-384` launch digest to the attestation service and received a signed launch artifacts bundle containing the [<mark style="color:blue;">ID-Block</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#id-block) and [<mark style="color:blue;">Auth-Block</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#auth-block).
* **Created and verified a running attested VM:** Created a Confidential VM on <code class="expression">space.vars.ionos\_cloud</code> and confirmed it passed the full attestation handshake and booted into a trusted state.

## Before you begin

Ensure you meet the following requirements before starting:

* Have an active <code class="expression">space.vars.ionos\_cloud</code> account with Confidential VM enabled in the Frankfurt-East `de/fra/2` region. Contact [<mark style="color:blue;">IONOS CLOUD Sales</mark>](https://docs.ionos.com/cloud/support/general-information/contact-information#sales) to enable it, then verify the feature is active:

  ```bash
  curl -s -H "Authorization: Bearer $IONOS_TOKEN" \
  "https://api.ionos.com/cloudapi/v6/datacenters/$DC_ID" \
  | jq '.properties.enabledFeatures'
  # Expected output: ["confidential-computing"]
  ```
* Use a Linux workstation running Debian 13+ or Ubuntu 24.04+ with the following packages installed:

  ```bash
  sudo apt-get install -y qemu-utils libguestfs-tools jq docker.io
  ```
* Install the latest Rust environment via rustup:

  ```bash
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
  source "$HOME/.cargo/env"
  rustup target add x86_64-unknown-linux-musl
  ```
* Install the SNPGuard build toolchain:

  ```bash
  sudo apt-get install -y \
    build-essential pkg-config libssl-dev protobuf-compiler \
    musl-tools musl-dev gcc-x86-64-linux-gnu clang llvm
  ```
* Clone the SNPGuard repository and build from source:

  ```bash
  git clone https://github.com/ionos-cloud/snp-guard.git
  cd snp-guard
  git submodule update --init --recursive
  make
  ```
* Provision a host with a stable, routable IP address where you will run the SNPGuard server. The Confidential VM must reach this host on every start.
* Export your <code class="expression">space.vars.ionos\_cloud</code> credentials and your datacenter ID as environment variables:

  ```bash
  export IONOS_TOKEN="your-api-token"
  export DC_ID="your-datacenter-id"
  ```
* Obtain your <code class="expression">space.vars.ionos\_cloud</code> FTP credentials from the [<mark style="color:blue;">Data Center Designer (DCD)</mark>](https://docs.ionos.com/cloud/set-up-ionos-cloud/data-center-designer) in **Storage & Backup** > **Images & Snapshots**. Select the **FTP Upload Image** tab in the **Manage Images and Snapshots** window. These are separate from your API credentials. For more information, see [<mark style="color:blue;">Upload an Image via the FTP</mark>](https://docs.ionos.com/cloud/backup-and-storage/images-snapshots/private-images/how-tos/upload-image-via-ftp).

## Cost considerations

The following resources used in this tutorial are billable:

* **Confidential VM:** <code class="expression">space.vars.ionos\_cloud</code> bills this per hour based on the vCPU and RAM configuration. Contact your <code class="expression">space.vars.ionos\_cloud</code> account manager for pricing specific to AMD SEV-SNP hardware.
* **Block Storage (Balanced or Performance):** <code class="expression">space.vars.ionos\_cloud</code> bills this per GB per month.

The SNPGuard server is self-hosted. Compute costs depend on the infrastructure you choose for it.

## Procedure

{% stepper %}
{% step %}

### Deploy the SNPGuard attestation server

The SNPGuard server stores your volume master key and verifies every VM startup. It must be reachable from the VM's network at every boot. Deploy it before any other step.

1. Generate TLS certificates for the server. Replace `YOUR-SERVER-IP` with the IP address of your server host, or use `--dns YOUR-HOSTNAME` if you are using a hostname:

```bash
./scripts/generate-tls-certs.sh --output data/tls --ip YOUR-SERVER-IP
```

2. Start the server:

```bash
make run-server
```

3. On first start, the server generates a master password and prints it to `stdout`. Copy it, then:

* Navigate to the web dashboard at `https://YOUR-SERVER-HOST:3000`
* Log in with the master password
* Go to **Tokens** to create an API token for the CLI client

4. Save the token and server URL as environment variables:

```bash
export SNPGUARD_TOKEN="your-api-token"
export SNPGUARD_URL="https://YOUR-SERVER-HOST:3000"
```

{% hint style="success" %}
**Expected result:** The web dashboard is accessible at `https://YOUR-SERVER-HOST:3000` and shows an empty image registry.
{% endhint %}
{% endstep %}

{% step %}

### Configure the SNPGuard client

1. Configure snpguard-client to connect to your server. This step uses Trust On First Use (TOFU): the client captures and pins the server TLS certificate so all subsequent connections are bound to that exact certificate.

```bash
snpguard-client config login \
  --url $SNPGUARD_URL \
  --token $SNPGUARD_TOKEN
```

2. The client stores the server URL, API token, CA certificate, ingestion public key, and identity public key in `~/.config/snpguard/`. The conversion process embeds these values into the guest image; you cannot change them without a full rebuild.

{% hint style="warning" %}
**Important:** If your server uses a privately generated TLS certificate (recommended for production), the client pins the CA at login. This prevents wildcard certificate attacks common on shared hosting platforms.
{% endhint %}

{% hint style="success" %}
**Expected result:** The client prints the server certificate fingerprints, confirms the token is valid, and saves the configuration to `~/.config/snpguard/`.
{% endhint %}
{% endstep %}

{% step %}

### Download the base OS image

Download a standard Debian 13+ or Ubuntu 24.04+ cloud image. SNPGuard requires a guest kernel with AMD SEV-SNP support and currently supports images that use `initramfs-tools` (Debian and Ubuntu).

{% tabs %}
{% tab title="Debian 13" %}
For Debian 13 (Trixie), SEV-SNP is supported from "Trixie" onwards:

```bash
wget https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
```

{% endtab %}

{% tab title="Ubuntu 24.04 LTS" %}
For Ubuntu 24.04 LTS, SEV-SNP is supported from "Noble" onwards:

```bash
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Convert the base image to a confidential image

The `snpguard-image convert` command transforms the standard cloud image into a confidential-ready image. During conversion, the tool performs the following operations:

1. Encrypts the root filesystem with [<mark style="color:blue;">LUKS2</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#luks) using a randomly generated volume master key (VMK).
2. Seals the `VMK` with the attestation server's ingestion public key and stores the sealed `blob` in the guest image. The VMK is never stored in plaintext.
3. Injects the `snpguard-client` binary and the attestation hooks into the guest `initrd`.
4. Extracts the kernel, `initrd`, firmware, and kernel parameters to a local staging directory for use in the next step.

```bash
snpguard-image convert \
  --in-image debian-13-genericcloud-amd64.qcow2 \
  --out-image confidential.qcow2 \
  --out-staging ./staging \
  --firmware OVMF.AMDSEV.fd
```

{% hint style="warning" %}
**Important:**

* **Do not interrupt the process:** The conversion takes several minutes. Do not interrupt it.
* **Do not register the measurement yet:** The [<mark style="color:blue;">measurement</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#measurement) must reflect the `initrd` produced by this command, which includes the sealed VMK and the embedded attestation server URL. Registering before conversion causes every boot to fail attestation.
  {% endhint %}

{% hint style="success" %}
**Expected result:** The command completes without errors and produces `confidential.qcow2`. A staging directory contains `firmware-code.fd`, `vmlinuz`, `initrd.img`, and `kernel-params.txt`.
{% endhint %}
{% endstep %}

{% step %}

### Register the attestation record

Register the converted image with the SNPGuard server. This uploads the boot artifacts to the server, which computes the `SHA-384` launch digest of the firmware, kernel, `initrd`, and kernel parameters, generates and signs the [<mark style="color:blue;">ID-Block</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#id-block) and [<mark style="color:blue;">Auth-Block</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#auth-block) artifacts, and returns a signed `launch-artifacts.tar.gz` bundle.

```bash
snpguard-client manage register \
  --os-name Debian13-CVM \
  --vcpus 4 --vcpu-type EPYC-Turin \
  --allowed-smt \
  --min-tcb-bootloader 0 --min-tcb-tee 0 --min-tcb-snp 0 --min-tcb-microcode 0 \
  --staging-dir ./staging \
  --out-bundle ./launch-artifacts.tar.gz
```

{% hint style="info" %}
**Note:** `--allowed-smt` permits Simultaneous Multithreading (SMT) within the guest. This option is mandatory for all deployments on the <code class="expression">space.vars.ionos\_cloud</code> platform.
{% endhint %}

You can view the registered image and its computed measurement in the web dashboard at `$SNPGUARD_URL`. The record shows the registered vCPU configuration and the [<mark style="color:blue;">guest policy</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#guest-policy) bitmask.

{% hint style="info" %}
**Note:** The `--os-name` value is a descriptive label for the OS or VM. Use a name that identifies the image version. Example: `debian13-cc-v1`.
{% endhint %}

{% hint style="success" %}
**Expected result:** The command produces `launch-artifacts.tar.gz`. The new record appears in the web dashboard with status **Enabled** and a request count of zero.
{% endhint %}
{% endstep %}

{% step %}

### Embed the launch artifacts into the image

Write the signed launch artifacts into a dedicated `LAUNCH_ARTIFACTS` partition inside the confidential image. <code class="expression">space.vars.ionos\_cloud</code> reads this partition at VM start time to configure the hardware-enforced launch.

```bash
snpguard-image embed \
  --image ./confidential.qcow2 \
  --in-bundle ./launch-artifacts.tar.gz
```

The embed command creates a 512 MB `ext4` partition labelled `LAUNCH_ARTIFACTS` with an `A/B` directory structure. Active artifacts are placed in `/A` and a symbolic link (symlink) `/artifacts -> A` points to the active set. This structure enables atomic in-place artifact renewal from inside a running VM using `snpguard-client attest renew`.

{% hint style="warning" %}
**Important:** The image is now sealed. Do not modify `confidential.qcow2` after this step. Any change to the firmware, kernel, `initrd`, or kernel parameters produces a different measurement and breaks attestation at every subsequent start.
{% endhint %}

{% hint style="success" %}
**Expected result:** The command completes without errors and the `LAUNCH_ARTIFACTS` partition is present in the image with the correct label, `A/B` directory structure, and symlink.
{% endhint %}
{% endstep %}

{% step %}

### Upload the confidential image to IONOS CLOUD

Upload the confidential image to the <code class="expression">space.vars.ionos\_cloud</code> FTP endpoint for your region. Confidential VM images must go to the `/confidential-images/` folder. The standard pipeline processes images placed in the standard `/images/` folder, but it cannot handle `LUKS2`-encrypted confidential images.

```bash
# Replace `ftp-fra.ionos.com` with the FTP hostname for your region (available from DCD)
curl --ssl-reqd -T confidential.qcow2 -u ${user:password} ftp://ftp-fra.ionos.com/confidential-images/confidential.qcow2
```

At the FTP prompt:

```ftp
ftp> cd /confidential-images/
ftp> put confidential.qcow2
ftp> bye
```

The upload handler validates the `LAUNCH_ARTIFACTS` partition, checks that all required files are present with exact filenames, and verifies that `launch-config.json` contains the correct `guest-policy` value. If any check fails, the handler rejects the image and reports which check failed.

{% hint style="success" %}
**Expected result:** The image uploads successfully. After validation, it appears in the <code class="expression">space.vars.ionos\_cloud</code> image list with `requiredFeatures: ["confidential-computing"]`.
{% endhint %}
{% endstep %}

{% step %}

### Create the Confidential VM

VM creation is a two-call sequence. You must first create the startup volume from your uploaded image, then create the VM referencing that volume.

#### 1. Find your uploaded image ID

```bash
curl -s -H "Authorization: Bearer $IONOS_TOKEN" \
  https://api.ionos.com/cloudapi/v6/images \
  | jq '.items[]
      | select(.properties.requiredFeatures[]? == "confidential-computing")
      | {id, name: .properties.name}'
```

#### 2. Create the startup volume

Replace `YOUR-IMAGE-ID` with the image ID from the previous command. Set `size` to at least the image size in GB, plus headroom for your application data.

{% hint style="info" %}
**Note:** Remember to note the `id` value from the response and wait for the volume to reach `AVAILABLE` status before continuing.
{% endhint %}

```bash
curl -s -X POST \
  -H "Authorization: Bearer $IONOS_TOKEN" \
  -H "Content-Type: application/json" \
  https://api.ionos.com/cloudapi/v6/datacenters/$DC_ID/volumes \
  -d '{
    "properties": {
      "name": "cc-boot-volume",
      "type": "Balanced",
      "size": 30,
      "image": "YOUR-IMAGE-ID"
    }
  }'
```

#### 3. Create the Confidential VM

Replace `YOUR-VOLUME-ID` with the volume ID from above and `YOUR-ATTESTATION-URL` with the HTTPS URL of your SNPGuard server.

```bash
curl -s -X POST \
  -H "Authorization: Bearer $IONOS_TOKEN" \
  -H "Content-Type: application/json" \
  https://api.ionos.com/cloudapi/v6/datacenters/$DC_ID/servers \
  -d '{
    "properties": {
      "name": "my-cc-vm",
      "type": "ENTERPRISE",
      "ram": 8192,
      "confidentialityMode": "ENABLED",
      "attestationServiceUrl": "YOUR-ATTESTATION-URL",
      "bootVolume": { "id": "YOUR-VOLUME-ID" }
    }
  }'
```

{% hint style="info" %}
**Important:** The system automatically derives both values from the `vcpu-count` and `vcpu-model` fields in the `launch-config.json` embedded in the `LAUNCH_ARTIFACTS` partition. Do not set `cores` or `cpuFamily` in the API request.
{% endhint %}

{% hint style="success" %}
**Expected result:** The API returns a server object with `status: RUNNING`. The VM begins the hardware-enforced boot: OVMF initialises, the AMD Secure Processor computes the launch measurement, the `initrd` starts, the SNPGuard client contacts your attestation server, attestation passes, the VMK is released, `LUKS2` unlocks the root filesystem, and the OS starts.
{% endhint %}
{% endstep %}
{% endstepper %}

{% hint style="success" %}
**Result:** You now have a running, attested Confidential VM on <code class="expression">space.vars.ionos\_cloud</code>. The VM:

* Boots only after presenting a hardware-signed AMD SEV-SNP attestation report that passes all AMD SEV-SNP verification checks against your registered measurement policy.
* Holds its root filesystem in a `LUKS2` container that can only be unlocked by the VMK released by your SNPGuard server. <code class="expression">space.vars.ionos\_cloud</code> never holds or accesses this key.
* Runs inside an AMD SEV-SNP [<mark style="color:blue;">Trusted Execution Environment (TEE)</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms#trusted-execution-environment-tee) where the AMD CPU encrypts all VM memory. The architecture physically excludes the hypervisor, host OS, and <code class="expression">space.vars.ionos\_cloud</code> from the decryption path.
  {% endhint %}

## Verification

After the VM reaches `RUNNING` status, verify that attestation succeeded and the VM is accessible.

### 1. Confirm the request count incremented in the web dashboard

Navigate to `$SNPGUARD_URL`, find your registration, and confirm the **Request count** has increased to one. This counter only increments when a VM passes all AMD SEV-SNP verification checks.

### 2. Check the SNPGuard server logs for a successful attestation event

Review the server log output.

{% hint style="success" %}
**Expected result:** A log line containing `attestation success` with the image ID matching your registered record.
{% endhint %}

### 3. SSH into the VM

SSH into your VM using the following command:

```bash
ssh debian@YOUR-VM-IP
```

{% hint style="success" %}
**Expected result:** You reach a shell prompt inside the VM. SSH access confirms the OS fully booted, which only happens after the attestation handshake completes and `LUKS2` unlocks the root filesystem.
{% endhint %}

{% hint style="warning" %}
**Important:** The VM may be stuck in an attestation retry loop. Verify that your SNPGuard server is reachable from the VM subnet and that the registered measurement matches the artifacts embedded in the image. For troubleshooting guidance, see [<mark style="color:blue;">Attest a Confidential VM</mark>](https://docs.ionos.com/cloud/compute-services/compute-engine/confidential-computing-vm/api-how-tos/attest-confidential-vm) .
{% endhint %}

### 4. Verify `LUKS2` is active inside the VM

```bash
lsblk -o NAME,TYPE,FSTYPE,MOUNTPOINT | grep -E "crypt|LUKS"
```

{% hint style="success" %}
**Expected result:** A `crypt` device mounted at `/`, confirming the root filesystem is running inside the `LUKS2` container and the VMK, released during attestation, unlocked it.
{% endhint %}

## Conclusion

In this tutorial, you deployed the complete SNPGuard attestation workflow for <code class="expression">space.vars.ionos\_cloud</code> Confidential VM. The setup:

* Automates the full lifecycle from image preparation through attested boot, replacing manual measurement calculation and key delivery with an auditable, reproducible pipeline.
* Enforces a hardware-level trust boundary: the VM cannot start into a trusted state without presenting a genuine AMD SEV-SNP attestation report that matches your registered measurement.
* Gives you exclusive control over the VMK. <code class="expression">space.vars.ionos\_cloud</code> has no access to the attestation service, the VMK, or the decrypted VM memory.

## Next steps

To extend this setup, see:

* [<mark style="color:blue;">Glossary</mark>](https://docs.ionos.com/cloud/support/general-information/glossary-of-terms)
* [<mark style="color:blue;">Attest a Confidential VM</mark>](https://docs.ionos.com/cloud/compute-services/compute-engine/confidential-computing-vm/api-how-tos/attest-confidential-vm)
* [<mark style="color:blue;">Prepare a Confidential VM Image</mark>](https://docs.ionos.com/cloud/compute-services/compute-engine/confidential-computing-vm/api-how-tos/prepare-confidential-vm-image)
* [<mark style="color:blue;">Security and trust model</mark>](https://docs.ionos.com/cloud/compute-services/compute-engine/confidential-computing-vm/overview/security-and-trust-model)
* [<mark style="color:blue;">SNPGuard repository</mark>](https://github.com/ionos-cloud/snp-guard)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ionos.com/cloud/tutorials/compute-services/confidential-computing-vm/deploy-confidential-vm-with-snpguard.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
