All Products
Search
Document Center

Elastic Compute Service:Deploy CoCo on Alibaba Cloud bare metal

Last Updated:May 15, 2026

Overview

Containerized workloads in shared environments face threats that traditional container isolation cannot address, including malicious administrators, compromised kernels, and firmware-level attacks.

The CNCF Confidential Containers (CoCo) project encapsulates Kubernetes pods in hardware-based confidential VMs. It encrypts runtime memory to make it inaccessible to the host, and provides remote attestation to verify environment integrity before injecting secrets.

This topic walks you through deploying CoCo on an ecs.ebmg8i.48xlarge bare metal instance with Intel TDX to build a trusted, isolated runtime for containerized workloads.

Architecture

image

Each Kubernetes pod runs inside a TDX confidential VM that protects data at runtime. All processing occurs within this secure boundary.

Objectives

  • Deploy a single-node CoCo cluster (version v0.17.0).

  • Set up a single-node Kubernetes cluster (v1.32.0) using Kubeadm.

Procedure

Step 1: Create a TDX bare metal instance

Create an 8th-generation Alibaba Cloud bare metal instance with Intel TDX support.

  1. Go to the instance purchase page.

  2. Select a billing method, region, and availability zone. The required community image is available only in specific zones. For this example, select Beijing Zone I.

  3. In the Instance section, click Elastic Bare Metal Server and select the ecs.ebmg8i.48xlarge instance type.

  4. In the Image section, select Community Image and search for image ID m-2ze2ucup4c5bvgx751lx. Select the Confidential VM checkbox to enable TDX.

    Important

    This is a custom Ubuntu-based image with pre-installed TDX drivers. The default SSH user is ubuntu, not root.

  5. Configure the network, storage, bandwidth, security group, and management settings. See Configuration Item Descriptions.

  6. Review the overall configuration on the right side of the page. Configure options such as the subscription duration and verify that all settings meet your requirements.

  7. Click Confirm Order to create the instance.

    Instance creation takes 3 to 5 minutes. Go to the Instances page in the console to check the status. The instance is ready when its status changes to Running.

Step 2: Prepare the Kubernetes node

Connect to the instance and install the required software to set it up as a single-node Kubernetes cluster.

  1. Install containerd

    # Update the package sources and install dependencies.
    sudo apt-get update
    sudo apt-get install -y ca-certificates curl
    
    # Add Docker's official GPG key.
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    
    # Add Docker's APT repository.
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
      
    # Install containerd.
    sudo apt-get update
    sudo apt-get install -y containerd.io
  2. Configure containerd Generate the default configuration file and modify key parameters for Kubernetes compatibility.

    # Generate the default configuration file.
    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml
    
    # Replace the Kubernetes image registry with an Alibaba Cloud mirror to accelerate image pulls.
    sudo sed -i -E 's#registry.k8s.io#registry.aliyuncs.com/google_containers#g' /etc/containerd/config.toml
    
    # Change the cgroup driver to systemd to meet Kubernetes requirements.
    sudo sed -i 's#SystemdCgroup = false#SystemdCgroup = true#g' /etc/containerd/config.toml
    
    # Restart the containerd service to apply the changes.
    sudo systemctl restart containerd
  3. Configure kernel parameters Disable the swap partition and load the required kernel modules for Kubernetes networking and runtime.

    # Temporarily disable swap.
    sudo swapoff -a
    # Permanently disable swap by commenting out the swap entry in the fstab file.
    sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
    
    # Load the overlay and br_netfilter kernel modules.
    sudo modprobe overlay
    sudo modprobe br_netfilter
    
    # Configure the modules to load automatically at boot.
    sudo tee /etc/modules-load.d/k8s.conf <<EOF
    overlay
    br_netfilter
    EOF
    
    # Configure kernel parameters to allow IP forwarding and bridged traffic processing.
    sudo tee /etc/sysctl.d/kubernetes.conf <<EOT
    net.bridge.bridge-nf-call-ip6tables = 1
    net.bridge.bridge-nf-call-iptables = 1
    net.ipv4.ip_forward = 1
    EOT
    
    # Apply all sysctl configurations.
    sudo sysctl --system

Step 3: Install a single-node Kubernetes cluster

Use kubeadm to set up a single-node Kubernetes cluster.

  1. Install Kubernetes components Install kubeadm, kubelet, and kubectl.

    # Add the GPG key for the Kubernetes APT repository.
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    
    # Add the Kubernetes APT repository.
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
    # Install the Kubernetes tools.
    sudo apt-get update
    sudo apt-get install -y kubectl kubeadm kubelet
    sudo apt-mark hold kubelet kubeadm kubectl
  2. Initialize the control plane Use kubeadm init to initialize the control plane.

    # Get the IP address of the primary network interface to use as the API server's advertise address.
    # Note: This command assumes the primary interface is eth0. If your environment is different, replace it with the correct IP address.
    NODE_IP=$(ip -o -4 addr show dev eth0 | awk '{split($4,a,"/");print a[1]}')
    
    # Initialize the cluster.
    sudo kubeadm init --pod-network-cidr=10.10.0.0/16 \
      --apiserver-advertise-address ${NODE_IP} \
      --kubernetes-version v1.32.0 \
      --image-repository registry.aliyuncs.com/google_containers

    After initialization, follow the output instructions to configure kubectl access credentials.

    mkdir -p $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
  3. Install a network plugin Deploy the Flannel CNI plugin for pod-to-pod communication.

    # Install the Flannel network plugin.
    export KUBECONFIG=/etc/kubernetes/admin.conf
    
    FLANNEL_VERSION=v0.27.4
    wget https://github.com/flannel-io/flannel/releases/download/${FLANNEL_VERSION}/kube-flannel.yml
    
    # Modify the Flannel manifest to set the correct pod network CIDR and use a regional image mirror.
    sed -i -E 's#([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]+#10.10.0.0/16#g' kube-flannel.yml
    sed -i -E 's#ghcr.io/flannel-io#confidential-ai-registry.cn-shanghai.cr.aliyuncs.com/product#g' kube-flannel.yml
    
    # Deploy Flannel.
    kubectl apply -f kube-flannel.yml
  4. Remove the control plane taint Allow workloads on this single-node cluster by removing the NoSchedule taint from the control plane node.

    NODE_NAME=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
    kubectl taint nodes $NODE_NAME node-role.kubernetes.io/control-plane-

Step 4: Deploy CoCo components

Choose either the Helm chart or Operator method to deploy CoCo.

Helm chart

Deploy CoCo with a Helm chart.

  1. Install Helm

    curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
    chmod 700 get_helm.sh
    ./get_helm.sh
  2. Configure the CoCo Helm chart Clone the CoCo charts repository and create a custom values.yaml to enable TDX runtimes.

    # Clone the charts repository and check out the specified version.
    git clone https://github.com/confidential-containers/charts.git
    cd charts
    git reset --hard v0.17.0
    
    # Create a TDX-specific configuration file.
    cat << EOF > values-tdx.yaml
    architecture: x86_64
    
    # Point the CoCo image registry to a regional mirror address.
    kata-as-coco-runtime:
      image:
        reference: registry-cn-hangzhou.ack.aliyuncs.com/dev/coco-kata-deploy
      imagePullPolicy: Always
      k8sDistribution: k8s
      debug: false
    
      # Snapshotter configuration
      snapshotter:
        setup: ["nydus"]
    
      # Enable the TDX-related shims.
      shims:
        qemu-tdx:
          enabled: true
          supportedArches:
            - amd64
          containerd:
            snapshotter: nydus
            forceGuestPull: false
          crio:
            guestPull: true
          agent:
            httpsProxy: ""
            noProxy: ""
        qemu-coco-dev:
          enabled: true
          supportedArches:
            - amd64
          allowedHypervisorAnnotations: []
          containerd:
            snapshotter: nydus
            forceGuestPull: false
          crio:
            guestPull: true
          agent:
            httpsProxy: ""
            noProxy: ""
        # Explicitly disable other unnecessary TEE shims.
        qemu-snp:
          enabled: false
        qemu-se:
          enabled: false
    
    # Enable RuntimeClass creation.
    runtimeClasses:
      enabled: true
      createDefault: false
      defaultName: "kata"
    # Default shim per architecture
      defaultShim:
        amd64: qemu-tdx
    EOF
  3. Deploy CoCo Install CoCo with Helm using your custom configuration.

    helm install coco oci://ghcr.io/confidential-containers/charts/confidential-containers \
      -f values-tdx.yaml \
      --namespace coco-system \
      --create-namespace \
      --version 0.17.0
  4. Create the RuntimeClass The Helm chart does not create a default RuntimeClass. Manually create the required RuntimeClass objects for your pods.

    cat <<EOF | kubectl apply -f -
    apiVersion: node.k8s.io/v1
    kind: RuntimeClass
    metadata:
      name: kata-qemu-tdx
    handler: kata-qemu-tdx
    ---
    apiVersion: node.k8s.io/v1
    kind: RuntimeClass
    metadata:
      name: kata-qemu-coco-dev
    handler: kata-qemu-coco-dev
    EOF

Operator

mkdir -p kustomize && cd kustomize

mkdir release && mkdir -p ccruntime/default

cat <<EOF > release/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- "github.com/confidential-containers/operator/config/release?ref=v0.17.0"

images:
- name: quay.io/confidential-containers/operator
  newName: registry-cn-hangzhou.ack.aliyuncs.com/dev/coco-operator
EOF

cat <<EOF > ccruntime/default/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- "github.com/confidential-containers/operator/config/samples/ccruntime/default?ref=v0.17.0"

images:
- name: quay.io/confidential-containers/reqs-payload
  newName: registry-cn-hangzhou.ack.aliyuncs.com/dev/coco-reqs-payload
- name: quay.io/kata-containers/kata-deploy-ci
  newName: registry-cn-hangzhou.ack.aliyuncs.com/dev/coco-kata-deploy-ci
- name: quay.io/kata-containers/kata-deploy
  newName: registry-cn-hangzhou.ack.aliyuncs.com/dev/coco-kata-deploy
EOF

# Allow the current node to act as a worker.
for NODE_NAME in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
  kubectl label node $NODE_NAME node.kubernetes.io/worker=
done

kubectl apply -k release
kubectl apply -k ccruntime/default

Step 5: Deploy and verify a sample pod

Run a sample pod and verify that it runs in a TDX confidential VM.

  1. Label the node Label the worker node to indicate Kata runtime support.

    NODE_NAME=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
    kubectl label node $NODE_NAME katacontainers.io/kata-runtime=true
  2. Deploy a sample pod Create a pod that uses the TDX runtime via runtimeClassName in its spec.

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: coco-demo-pod
    spec:
      runtimeClassName: kata-qemu-tdx
      containers:
        - image: alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/alinux3:latest
          name: hello-alinux
          command:
            - "sleep"
            - "infinity"
    EOF
  3. Verify the deployment Confirm that the pod is running with the correct runtime class.

    # Wait for the pod status to become Running.
    kubectl get pod coco-demo-pod
    
    # Describe the pod and confirm that the "Runtime Class" field is "kata-qemu-tdx".
    kubectl describe pod coco-demo-pod | grep "Runtime Class"
  4. Verify TDX activation Check the kernel logs on the bare metal instance to confirm TDX initialization.

    # Run this command on the node.
    dmesg | grep -i tdx

    Output containing TDX module initialized or similar messages confirms that the TDX environment is active and the Kata runtime is using it for confidential VMs.

Costs and risks

  • Cost breakdown: The primary cost comes from the ecs.ebmg8i.48xlarge bare metal instance. Its high specifications suit production workloads with strict security and performance requirements, but increase evaluation and testing costs.

  • Limitations and risks:

    • Hardware and region dependency: This solution requires a specific bare metal instance type and supporting availability zones.

    • Single point of failure: This tutorial uses a single-node cluster, which lacks high availability. Production deployments require a multi-node cluster with a high-availability architecture.

    • Version compatibility: CoCo, Kubernetes, and related component versions are tightly coupled. Upgrades require careful compatibility testing.