tutorial

Monitoring containerd-wasm with Vigilmon: Keeping Your WebAssembly Shims Healthy in Kubernetes

How to monitor containerd-wasm shim plugins with Vigilmon — tracking shim availability, Wasm runtime integration health, container lifecycle probes, and Kubernetes scheduling signals from outside your infrastructure.

containerd-wasm provides a family of containerd shim plugins that enable Kubernetes nodes to run WebAssembly workloads directly — using Spin, WasmEdge, Slight, and other Wasm runtimes — without wrapping them in a container process. Platform teams adopting Wasm-on-Kubernetes rely on these shims for every Wasm pod schedule: when a shim fails or mismatches the runtime binary, workloads stop scheduling silently while the control plane reports CreateContainerError or RunContainerError with cryptic messages. Vigilmon gives you external monitoring that watches the shim integration layer and validates that Wasm workload scheduling completes on time.

What You'll Build

  • Vigilmon HTTP monitors for the containerd service and shim availability
  • A heartbeat monitor validating that a Wasm workload schedules and runs end-to-end
  • A runtime binary health heartbeat to catch missing or mismatched shim executables
  • Alert channels routed to your platform engineering team

Prerequisites

  • containerd installed with containerd-wasm shims (e.g., containerd-shim-spin-v2, containerd-shim-wasmedge-v1)
  • A Kubernetes node with RuntimeClass objects referencing the Wasm shims
  • A free account at vigilmon.online

Why Monitoring containerd-wasm Matters

containerd-wasm shims operate at the boundary between Kubernetes scheduling and the Wasm runtime. Their failure modes are silent and often misdiagnosed as workload or image problems:

Missing shim binaries block all Wasm pod scheduling on a node. If containerd-shim-spin-v2 is absent from $PATH or the configured shim directory, every pod using runtimeClassName: spin fails with RunContainerError — but containerd itself remains healthy and reports no errors at the daemon level. Engineers debug pod YAML, images, and network policies before realizing the shim binary was never installed after a node rebuild.

Runtime version mismatches cause silent incompatibility failures. A shim compiled against Spin 2.3 may refuse workloads built for Spin 2.4, returning an obscure OCI runtime error rather than a version mismatch message. External probes that run a known-good Wasm workload catch this regression faster than pod-level error inspection.

containerd shim socket leaks accumulate over time and eventually exhaust file descriptors on busy nodes. Each containerd-wasm shim opens its own socket; a shim that crashes without cleanup leaves orphaned sockets. Monitoring the shim socket count alongside containerd memory usage surfaces this before node degradation.

RuntimeClass to shim mapping drift occurs when a Kubernetes upgrade or containerd reconfiguration removes or renames the shim handler registered for a RuntimeClass. Pods that previously scheduled successfully start failing with no change to their own spec. A lifecycle heartbeat tied to RuntimeClass scheduling detects this within one probe cycle.


Step 1: Monitor the containerd Service Health

containerd exposes a gRPC API; probe it via the containerd metrics endpoint or a simple systemd health check:

# Check containerd is running and responsive
ctr version 2>/dev/null | grep -q "Server:" && echo "healthy"

# Or check via systemd
systemctl is-active --quiet containerd && echo "healthy"

Expose containerd's built-in metrics endpoint for HTTP monitoring (available in containerd ≥ 1.6):

# /etc/containerd/config.toml — add or update the metrics section
[metrics]
  address = "0.0.0.0:1338"
  grpc_histogram = false
systemctl restart containerd
# Verify
curl -s http://localhost:1338/v1/metrics | head -5

In the Vigilmon dashboard:

  1. Go to Add Monitor → HTTP
  2. Configure:

| Field | Value | |---|---| | Name | containerd metrics | | URL | http://your-node-ip:1338/v1/metrics | | Method | GET | | Expected status | 200 | | Response body contains | containerd_running_containers | | Check interval | 1 minute | | Timeout | 10 seconds | | Alert after | 2 consecutive failures |


Step 2: Monitor Shim Binary Availability

Add an HTTP health endpoint that validates shim binaries are present and executable:

# /usr/local/bin/wasm-shim-health.sh
#!/bin/bash
set -euo pipefail

SHIMS=(
  "containerd-shim-spin-v2"
  "containerd-shim-wasmedge-v1"
  "containerd-shim-slight-v1"
)

all_ok=true
for shim in "${SHIMS[@]}"; do
  if ! command -v "$shim" >/dev/null 2>&1; then
    echo "MISSING: $shim"
    all_ok=false
  fi
done

if $all_ok; then
  echo "OK: all shims present"
  exit 0
else
  exit 1
fi

Serve the check result over HTTP using a minimal wrapper:

# /etc/systemd/system/wasm-shim-health.service
[Unit]
Description=containerd-wasm shim health HTTP endpoint
After=containerd.service

[Service]
ExecStart=/bin/bash -c 'while true; do \
  result=$(/usr/local/bin/wasm-shim-health.sh 2>&1); \
  code=$?; \
  if [ $code -eq 0 ]; then status="200 OK"; else status="503 Service Unavailable"; fi; \
  printf "HTTP/1.1 %s\r\nContent-Type: text/plain\r\n\r\n%s\n" "$status" "$result" | nc -l -p 9102 -q 1; \
done'
Restart=always

[Install]
WantedBy=multi-user.target
chmod +x /usr/local/bin/wasm-shim-health.sh
systemctl daemon-reload
systemctl enable --now wasm-shim-health

In Vigilmon, add a second HTTP monitor:

| Field | Value | |---|---| | Name | containerd-wasm shim binaries | | URL | http://your-node-ip:9102 | | Expected status | 200 | | Response body contains | OK: all shims present | | Check interval | 5 minutes | | Alert after | 1 failure |


Step 3: Heartbeat Monitor for Wasm Workload Scheduling

The most reliable validation is scheduling a real Wasm workload end-to-end. Create a probe that submits a minimal Wasm pod to Kubernetes and sends a heartbeat on success:

# /usr/local/bin/wasm-schedule-probe.sh
#!/bin/bash
set -euo pipefail

HEARTBEAT_URL="${WASM_HEARTBEAT_URL}"
NAMESPACE="wasm-probes"
POD_NAME="wasm-probe-$(date +%s)"

# Ensure namespace exists
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - >/dev/null

# Submit a minimal Spin hello-world pod
kubectl run "$POD_NAME" \
  --namespace="$NAMESPACE" \
  --image=ghcr.io/deislabs/containerd-wasm-shims/examples/spin-rust-hello:latest \
  --restart=Never \
  --overrides='{"spec":{"runtimeClassName":"spin"}}' \
  --timeout=60s 2>/dev/null

# Wait for completion
if kubectl wait pod/"$POD_NAME" \
  --namespace="$NAMESPACE" \
  --for=condition=Ready \
  --timeout=30s 2>/dev/null; then
  kubectl delete pod "$POD_NAME" --namespace="$NAMESPACE" --ignore-not-found >/dev/null 2>&1
  curl -fsS --max-time 10 "$HEARTBEAT_URL" -o /dev/null
  echo "$(date): Wasm schedule probe OK, heartbeat sent"
else
  kubectl delete pod "$POD_NAME" --namespace="$NAMESPACE" --ignore-not-found >/dev/null 2>&1
  echo "$(date): ERROR — Wasm pod failed to reach Ready state" >&2
  exit 1
fi
# /etc/systemd/system/wasm-probe.service
[Unit]
Description=containerd-wasm workload schedule probe
After=containerd.service

[Service]
Type=oneshot
EnvironmentFile=/etc/wasm-probe.env
ExecStart=/usr/local/bin/wasm-schedule-probe.sh
# /etc/systemd/system/wasm-probe.timer
[Unit]
Description=Run containerd-wasm schedule probe every 10 minutes

[Timer]
OnBootSec=5min
OnUnitActiveSec=10min
Unit=wasm-probe.service

[Install]
WantedBy=timers.target
# /etc/wasm-probe.env
WASM_HEARTBEAT_URL=https://vigilmon.online/heartbeat/YOUR_WASM_SCHEDULE_TOKEN
chmod +x /usr/local/bin/wasm-schedule-probe.sh
systemctl daemon-reload
systemctl enable --now wasm-probe.timer

Create the heartbeat monitor in Vigilmon:

  1. Add Monitor → Heartbeat
  2. Name: containerd-wasm workload scheduling
  3. Expected interval: 10 minutes
  4. Grace period: 5 minutes

Step 4: Configure Alert Channels

Route containerd-wasm alerts to your platform team:

  1. In Vigilmon: Alert Channels → Add Channel → Slack Webhook
  2. Add your #platform-engineering or #kubernetes-alerts webhook URL
  3. Assign to all containerd-wasm monitors

For shim binary failures (no shim = no Wasm scheduling on that node):

  1. Alert Channels → Add Channel → PagerDuty
  2. Route the containerd-wasm shim binaries monitor to on-call
  3. Shim binary absence warrants immediate response — every Wasm pod on that node fails silently until resolved

What You're Now Monitoring

| Component | Monitor Type | What It Detects | |---|---|---| | containerd metrics | HTTP GET | containerd daemon down or unresponsive | | Shim binary availability | HTTP GET | Missing or unexecutable shim binaries | | Wasm workload scheduling | Heartbeat (10 min) | End-to-end scheduling failure, RuntimeClass drift, runtime version mismatch |


Conclusion

containerd-wasm shims are invisible to standard Kubernetes observability. Pod errors are generic, containerd appears healthy, and the shim layer in between is never directly monitored. External monitoring with Vigilmon catches the failure modes that matter — missing binaries, runtime version mismatches, and scheduling regressions — before they cascade into multi-pod outages. The shim binary probe and workload scheduling heartbeat together give you the critical signals in under fifteen minutes of setup.

Get started free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →