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:
wrkon the host OS sending requests to Nginx running on the same host OSwrkon the host OS sending requests to Nginx running in a TDX VMwrkon 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.