Post

Intel TDX for dummies

Intel TDX for dummies

This is part of the TEEs for dummies series. Unlike SGX and TrustZone, which protect a single program at runtime, Intel Trusted Domain Extensions (TDX) protect an entire VM.

In TDX, a protected VM is called a trust domain (TD). The VM’s memory is transparently encrypted/decrypted using multi-key Total Memory Encryption (MKTME) at cache-line granularity within the CPU. Each VM is associated with a unique key which cannot be accessed by the hypervisor or other VMs. A central component called the TDX Module sits between TDs and the hypervisor and handles access control, TD context switches, and related operations.

Setup

  • First configure the default virtual network:
1
sudo virsh net-start default
  • To configure and enable TDX on your server, follow this README from Canonical. If TDX setup has already been done on your server (steps 1 to 4), you can skip them and run a TDX VM from step 5.

  • Once your TDX-enabled VM has been deployed, connect to it via SSH:

1
ssh -p 10022 tdx@localhost  # default password is 123456

You now have a VM whose memory is protected from the hypervisor. For remote attestation, see Canonical’s TDX README.

On the host, you can run ip neigh show dev virbr0 to check for your VM’s IP address.

Network I/O benchmarking

A common use case for confidential VMs is deploying secure web servers such as Nginx. To appreciate the cost of TDX, we evaluate three scenarios with wrk, a popular HTTP benchmarking tool:

  1. wrk on the host OS sending requests to Nginx running on the same host OS
  2. wrk on the host OS sending requests to Nginx running in a TDX VM
  3. wrk on the host OS sending requests to Nginx running in a regular (non-TDX) VM

Build wrk as follows:

1
2
3
4
sudo apt update
sudo apt install openssl net-tools libssl-dev unzip -y
git clone https://github.com/wg/wrk.git && cd wrk
make

Nginx and wrk on the same host OS

1
2
sudo apt install nginx -y
sudo systemctl start nginx

If the Nginx port is already in use, modify /etc/nginx/sites-available/default to use another port, e.g. 8080:

1
2
listen 8080 default_server;
listen [::]:8080 default_server;

Test the config with sudo nginx -t and start with sudo systemctl start nginx. Nginx should then be reachable at http://127.0.0.1:8080.

1
./wrk -t4 -c100 -d30s http://127.0.0.1:8080/

This uses 4 threads to generate connections independently for 30 seconds. Focus on requests per second. An example result on my host:

1
2
3
4
5
6
7
8
Running 30s test @ http://127.0.0.1:8080/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   646.74us    0.95ms  27.51ms   92.63%
    Req/Sec    40.85k     5.43k   70.05k    68.92%
  4879138 requests in 30.01s, 49.63GB read
Requests/sec: 162588.64
Transfer/sec:      1.65GB

Nginx in a TDX VM and wrk on the host

Create and log into a TDX VM as described above. Install and set up Nginx similarly. In this example the VM IP is 192.168.122.100 and Nginx listens on port 8080:

1
./wrk -t4 -c100 -d30s http://192.168.122.100:8080/

To verify that the VM is actually receiving wrk requests, check the Nginx access logs:

1
sudo tail -f /var/log/nginx/access.log

You should see many entries, for example:

1
192.168.122.1 - - [20/Aug/2025:10:11:55 +0000] "GET / HTTP/1.1" 200 615 "-" "-"

The host IP here is 192.168.122.1, so the TDX VM is receiving the requests. Example wrk result:

1
2
3
4
5
6
7
8
Running 30s test @ http://192.168.122.100:8080/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.22ms    2.71ms  32.92ms   78.31%
    Req/Sec     8.78k     1.14k   14.80k    71.33%
  1049350 requests in 30.02s, 862.63MB read
Requests/sec:  34951.73
Transfer/sec:     28.73MB

Throughput drops compared to Nginx on the host, which is expected: you now pay for virtualization plus TDX.

Nginx in a non-TDX VM

Repeat the same Nginx setup in a regular VM and run the same wrk command. Comparing host vs regular VM vs TDX VM is the useful dummy-level takeaway: isolation costs performance.

Benchmarking with UnixBench

1
2
3
4
5
git clone https://github.com/kdlucas/byte-unixbench.git
sudo apt update
sudo apt install build-essential libgcc-12-dev
cd byte-unixbench/UnixBench
./Run syscall

Example results:

  • TDX VM: System Call Overhead INDEX 12500.3
  • Regular VM (32 CPUs): INDEX 12534.3
  • Bare metal: INDEX 13212.1

The baseline is a SPARCstation 20-61, so these index numbers are relative to that old machine, not an absolute syscall rate. The useful comparison is across the three setups: TDX introduces some syscall overhead vs a regular VM, and both VMs are slower than bare metal.

The GPU TEE on Nvidia H100 is typically used together with Intel TDX or AMD SEV-SNP. Canonical’s TDX guide is a reasonable starting point if you later want to attach a confidential GPU.

More resources

This post is licensed under CC BY 4.0 by the author.