WebAssembly (WASM) is revolutionizing application deployment by enabling near-native performance with language independence and strong sandboxing. This comprehensive guide demonstrates building a production-ready WebAssembly runtime platform using Wasmtime on Rocky Linux, covering module management, security, performance optimization, and integration with cloud-native ecosystems.
Understanding WebAssembly and Wasmtime
WebAssembly is a binary instruction format designed for safe, fast execution:
- Language Agnostic: Compile from Rust, Go, C++, and more
- Portable: Run anywhere with consistent behavior
- Secure: Strong sandboxing and capability-based security
- Fast: Near-native performance with ahead-of-time compilation
- Lightweight: Smaller than containers, faster startup
Wasmtime Architecture
Wasmtime is a production-ready WebAssembly runtime:
- Cranelift: Fast compiler backend
- WASI: WebAssembly System Interface for system calls
- Security: Capability-based security model
- Performance: Optimizing compiler with caching
- Embedding: Easy integration into applications
Prerequisites
Before deploying Wasmtime:
- Rocky Linux 9 with kernel 5.10+
- Minimum 4GB RAM and 20GB storage
- Development tools installed
- Rust toolchain (for WASM development)
- Basic understanding of WebAssembly concepts
Installing Wasmtime on Rocky Linux
System Preparation
# Update system packages
sudo dnf update -y
# Install development dependencies
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y \
cmake \
clang \
lld \
pkg-config \
openssl-devel \
python3-pip \
jq
# Install performance monitoring tools
sudo dnf install -y \
perf \
sysstat \
htop \
iotop
# Configure system limits
cat <<EOF | sudo tee /etc/security/limits.d/wasmtime.conf
* soft nofile 65536
* hard nofile 65536
* soft nproc 32768
* hard nproc 32768
EOF
# Apply sysctl optimizations
cat <<EOF | sudo tee /etc/sysctl.d/99-wasmtime.conf
vm.max_map_count = 262144
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
EOF
sudo sysctl -p /etc/sysctl.d/99-wasmtime.conf
Installing Wasmtime
# Install Wasmtime using official installer
curl https://wasmtime.dev/install.sh -sSf | bash
# Add to PATH
echo 'export PATH="$HOME/.wasmtime/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Verify installation
wasmtime --version
# Install additional tools
# WASM-tools for module manipulation
cargo install wasm-tools
# wasm-pack for building WASM modules
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# Install wabt (WebAssembly Binary Toolkit)
git clone --recursive https://github.com/WebAssembly/wabt
cd wabt
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install
cd ../..
Building a WASM Module Management System
Module Registry Service
// src/registry.rs - WASM module registry
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use anyhow::{Result, Context};
use serde::{Deserialize, Serialize};
use sha2::{Sha256, Digest};
use wasmtime::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleMetadata {
pub name: String,
pub version: String,
pub hash: String,
pub size: u64,
pub imports: Vec<String>,
pub exports: Vec<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub permissions: ModulePermissions,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModulePermissions {
pub filesystem: Vec<String>,
pub network: bool,
pub env_vars: Vec<String>,
pub memory_limit: u64,
pub cpu_limit: f32,
}
pub struct ModuleRegistry {
modules: Arc<RwLock<HashMap<String, ModuleMetadata>>>,
storage_path: PathBuf,
engine: Engine,
}
impl ModuleRegistry {
pub fn new(storage_path: impl AsRef<Path>) -> Result<Self> {
let mut config = Config::new();
config.wasm_threads(true);
config.wasm_simd(true);
config.wasm_bulk_memory(true);
config.cranelift_opt_level(OptLevel::Speed);
let engine = Engine::new(&config)?;
Ok(Self {
modules: Arc::new(RwLock::new(HashMap::new())),
storage_path: storage_path.as_ref().to_path_buf(),
engine,
})
}
pub fn register_module(&self, name: &str, version: &str, wasm_bytes: &[u8]) -> Result<ModuleMetadata> {
// Validate WASM module
let module = Module::new(&self.engine, wasm_bytes)
.context("Failed to compile WASM module")?;
// Calculate hash
let mut hasher = Sha256::new();
hasher.update(wasm_bytes);
let hash = format!("{:x}", hasher.finalize());
// Extract module information
let imports = module.imports()
.map(|import| format!("{}::{}", import.module(), import.name()))
.collect();
let exports = module.exports()
.map(|export| export.name().to_string())
.collect();
// Create metadata
let metadata = ModuleMetadata {
name: name.to_string(),
version: version.to_string(),
hash,
size: wasm_bytes.len() as u64,
imports,
exports,
created_at: chrono::Utc::now(),
permissions: ModulePermissions {
filesystem: vec![],
network: false,
env_vars: vec![],
memory_limit: 256 * 1024 * 1024, // 256MB default
cpu_limit: 1.0,
},
};
// Store module
let module_path = self.storage_path
.join(&metadata.name)
.join(&metadata.version);
std::fs::create_dir_all(&module_path)?;
std::fs::write(module_path.join("module.wasm"), wasm_bytes)?;
let metadata_json = serde_json::to_string_pretty(&metadata)?;
std::fs::write(module_path.join("metadata.json"), metadata_json)?;
// Update registry
let key = format!("{}:{}", name, version);
self.modules.write().unwrap().insert(key, metadata.clone());
Ok(metadata)
}
pub fn get_module(&self, name: &str, version: &str) -> Result<Vec<u8>> {
let module_path = self.storage_path
.join(name)
.join(version)
.join("module.wasm");
std::fs::read(&module_path)
.with_context(|| format!("Module {}:{} not found", name, version))
}
}
WASM Runtime Manager
// src/runtime.rs - WASM runtime management
use wasmtime::*;
use wasmtime_wasi::{WasiCtx, WasiCtxBuilder};
use std::sync::Arc;
use anyhow::Result;
pub struct RuntimeConfig {
pub memory_limit: u64,
pub fuel_limit: Option<u64>,
pub epoch_deadline: Option<u64>,
pub allowed_paths: Vec<String>,
pub env_vars: Vec<(String, String)>,
}
pub struct WasmRuntime {
engine: Engine,
config: RuntimeConfig,
}
impl WasmRuntime {
pub fn new(config: RuntimeConfig) -> Result<Self> {
let mut engine_config = Config::new();
// Enable optimizations
engine_config.cranelift_opt_level(OptLevel::Speed);
engine_config.wasm_threads(true);
engine_config.wasm_simd(true);
// Enable fuel metering if specified
if config.fuel_limit.is_some() {
engine_config.consume_fuel(true);
}
// Enable epoch interruption
engine_config.epoch_interruption(true);
let engine = Engine::new(&engine_config)?;
Ok(Self { engine, config })
}
pub fn create_instance(&self, module_bytes: &[u8]) -> Result<WasmInstance> {
let module = Module::new(&self.engine, module_bytes)?;
// Create store with limits
let mut store = Store::new(&self.engine, WasmState::new());
// Set memory limits
store.limiter(|state| &mut state.limits);
// Set fuel if configured
if let Some(fuel) = self.config.fuel_limit {
store.add_fuel(fuel)?;
}
// Create WASI context
let wasi = self.create_wasi_context()?;
store.data_mut().wasi = Some(wasi);
// Create linker and add WASI
let mut linker = Linker::new(&self.engine);
wasmtime_wasi::add_to_linker(&mut linker, |state: &mut WasmState| {
state.wasi.as_mut().unwrap()
})?;
// Instantiate module
let instance = linker.instantiate(&mut store, &module)?;
Ok(WasmInstance {
store,
instance,
module,
})
}
fn create_wasi_context(&self) -> Result<WasiCtx> {
let mut builder = WasiCtxBuilder::new();
// Configure filesystem access
for path in &self.config.allowed_paths {
builder = builder.preopened_dir(
Dir::open_ambient_dir(path, ambient_authority())?,
path,
)?;
}
// Set environment variables
for (key, value) in &self.config.env_vars {
builder = builder.env(key, value)?;
}
// Inherit stdio
builder = builder.inherit_stdio();
Ok(builder.build())
}
}
pub struct WasmInstance {
store: Store<WasmState>,
instance: Instance,
module: Module,
}
pub struct WasmState {
wasi: Option<WasiCtx>,
limits: StoreLimits,
}
impl WasmState {
fn new() -> Self {
Self {
wasi: None,
limits: StoreLimitsBuilder::new()
.memory_size(256 * 1024 * 1024) // 256MB
.table_elements(10000)
.instances(100)
.memories(10)
.build(),
}
}
}
Deploying WASM Applications
HTTP Server with WASM Handlers
// src/server.rs - HTTP server with WASM request handlers
use axum::{
routing::{get, post},
Router,
extract::{Path, State},
response::Json,
http::StatusCode,
};
use std::sync::Arc;
use tokio::sync::RwLock;
use serde_json::Value;
pub struct WasmServer {
runtime: Arc<WasmRuntime>,
registry: Arc<ModuleRegistry>,
handlers: Arc<RwLock<HashMap<String, WasmHandler>>>,
}
pub struct WasmHandler {
module_name: String,
module_version: String,
instance: Arc<RwLock<WasmInstance>>,
}
impl WasmServer {
pub async fn new(runtime: WasmRuntime, registry: ModuleRegistry) -> Result<Self> {
Ok(Self {
runtime: Arc::new(runtime),
registry: Arc::new(registry),
handlers: Arc::new(RwLock::new(HashMap::new())),
})
}
pub async fn register_handler(
&self,
route: &str,
module_name: &str,
module_version: &str,
) -> Result<()> {
// Load module from registry
let module_bytes = self.registry.get_module(module_name, module_version)?;
// Create instance
let instance = self.runtime.create_instance(&module_bytes)?;
// Register handler
let handler = WasmHandler {
module_name: module_name.to_string(),
module_version: module_version.to_string(),
instance: Arc::new(RwLock::new(instance)),
};
self.handlers.write().await.insert(route.to_string(), handler);
Ok(())
}
pub async fn start(self: Arc<Self>, addr: &str) -> Result<()> {
let app = Router::new()
.route("/health", get(health_check))
.route("/modules", get(list_modules))
.route("/modules/:name/:version", post(upload_module))
.route("/invoke/:handler", post(invoke_handler))
.with_state(self);
let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(listener, app).await?;
Ok(())
}
}
async fn invoke_handler(
Path(handler): Path<String>,
State(server): State<Arc<WasmServer>>,
Json(input): Json<Value>,
) -> Result<Json<Value>, StatusCode> {
let handlers = server.handlers.read().await;
let handler = handlers.get(&handler)
.ok_or(StatusCode::NOT_FOUND)?;
let mut instance = handler.instance.write().await;
// Call WASM function
let func = instance.instance
.get_func(&mut instance.store, "handle_request")
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
// Serialize input
let input_json = serde_json::to_string(&input)
.map_err(|_| StatusCode::BAD_REQUEST)?;
// Allocate memory for input
let alloc_func = instance.instance
.get_func(&mut instance.store, "alloc")
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
let input_len = input_json.len() as i32;
let input_ptr = alloc_func
.typed::<i32, i32>(&instance.store)?
.call(&mut instance.store, input_len)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// Write input to memory
let memory = instance.instance
.get_memory(&mut instance.store, "memory")
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
memory.write(&mut instance.store, input_ptr as usize, input_json.as_bytes())
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// Call handler
let result_ptr = func
.typed::<(i32, i32), i32>(&instance.store)?
.call(&mut instance.store, (input_ptr, input_len))
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// Read result
let result_len_ptr = result_ptr + 4;
let mut result_len_bytes = [0u8; 4];
memory.read(&instance.store, result_len_ptr as usize, &mut result_len_bytes)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let result_len = i32::from_le_bytes(result_len_bytes) as usize;
let mut result_bytes = vec![0u8; result_len];
memory.read(&instance.store, result_ptr as usize, &mut result_bytes)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// Parse result
let result: Value = serde_json::from_slice(&result_bytes)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(result))
}
Building WASM Modules
Example: Image Processing Module
// image_processor/src/lib.rs - WASM image processing module
use wasm_bindgen::prelude::*;
use image::{DynamicImage, ImageFormat};
#[wasm_bindgen]
pub struct ImageProcessor {
image: Option<DynamicImage>,
}
#[wasm_bindgen]
impl ImageProcessor {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self { image: None }
}
pub fn load_image(&mut self, data: &[u8]) -> Result<(), JsValue> {
self.image = image::load_from_memory(data)
.map_err(|e| JsValue::from_str(&e.to_string()))?
.into();
Ok(())
}
pub fn resize(&mut self, width: u32, height: u32) -> Result<(), JsValue> {
if let Some(img) = &self.image {
self.image = Some(img.resize(width, height, image::imageops::FilterType::Lanczos3));
Ok(())
} else {
Err(JsValue::from_str("No image loaded"))
}
}
pub fn blur(&mut self, sigma: f32) -> Result<(), JsValue> {
if let Some(img) = &self.image {
self.image = Some(img.blur(sigma));
Ok(())
} else {
Err(JsValue::from_str("No image loaded"))
}
}
pub fn to_bytes(&self, format: &str) -> Result<Vec<u8>, JsValue> {
if let Some(img) = &self.image {
let fmt = match format {
"png" => ImageFormat::Png,
"jpeg" => ImageFormat::Jpeg,
"webp" => ImageFormat::WebP,
_ => return Err(JsValue::from_str("Unknown format")),
};
let mut bytes = Vec::new();
img.write_to(&mut bytes, fmt)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
Ok(bytes)
} else {
Err(JsValue::from_str("No image loaded"))
}
}
}
// Export functions for non-wasm-bindgen usage
#[no_mangle]
pub extern "C" fn process_image(
input_ptr: *const u8,
input_len: usize,
operation: *const u8,
operation_len: usize,
) -> *mut u8 {
// Implementation for direct WASM calls
unsafe {
let input = std::slice::from_raw_parts(input_ptr, input_len);
let op = std::slice::from_raw_parts(operation, operation_len);
let op_str = std::str::from_utf8_unchecked(op);
let mut processor = ImageProcessor::new();
if processor.load_image(input).is_err() {
return std::ptr::null_mut();
}
match op_str {
"thumbnail" => {
let _ = processor.resize(200, 200);
}
"blur" => {
let _ = processor.blur(2.0);
}
_ => return std::ptr::null_mut(),
}
match processor.to_bytes("png") {
Ok(bytes) => {
let len = bytes.len();
let ptr = bytes.as_ptr();
std::mem::forget(bytes);
ptr as *mut u8
}
Err(_) => std::ptr::null_mut(),
}
}
}
Building and Optimizing WASM Modules
#!/bin/bash
# build_wasm_module.sh
# Set up Rust for WASM target
rustup target add wasm32-wasi
rustup target add wasm32-unknown-unknown
# Build the module
cargo build --target wasm32-wasi --release
# Optimize the WASM binary
wasm-opt -O3 \
--enable-simd \
--enable-threads \
--enable-bulk-memory \
target/wasm32-wasi/release/image_processor.wasm \
-o image_processor_optimized.wasm
# Strip debug information
wasm-strip image_processor_optimized.wasm
# Validate the module
wasm-validate image_processor_optimized.wasm
# Generate bindings for different languages
wasm-bindgen \
target/wasm32-wasi/release/image_processor.wasm \
--out-dir ./bindings \
--target nodejs
# Analyze module size
wasm-objdump -h image_processor_optimized.wasm
Security and Sandboxing
Capability-Based Security Configuration
# wasm_security_policy.toml
[default]
# Default security policy for all modules
max_memory = "256MB"
max_cpu_time = "30s"
max_file_handles = 10
network_access = false
[filesystem]
# Allowed filesystem paths (sandboxed)
read_paths = [
"/tmp/wasm-sandbox/input",
"/var/lib/wasm-data/readonly"
]
write_paths = [
"/tmp/wasm-sandbox/output"
]
temp_path = "/tmp/wasm-sandbox/temp"
[network]
# Network access rules
allowed_hosts = []
allowed_ports = []
max_connections = 0
[resources]
# Resource limits
max_threads = 1
max_memory_pages = 4096 # 256MB with 64KB pages
stack_size = "1MB"
fuel_limit = 1000000000 # Computational steps
[capabilities]
# Fine-grained capabilities
clock_time = true
random = true
env_vars = ["WASM_*", "APP_*"]
args = false
stdin = false
stdout = true
stderr = true
Security Monitor
// src/security.rs - Security monitoring for WASM execution
use std::time::{Duration, Instant};
use tokio::sync::mpsc;
pub struct SecurityMonitor {
tx: mpsc::Sender<SecurityEvent>,
}
#[derive(Debug)]
pub enum SecurityEvent {
MemoryLimitExceeded { module: String, requested: u64, limit: u64 },
CpuTimeExceeded { module: String, duration: Duration, limit: Duration },
UnauthorizedFileAccess { module: String, path: String, operation: String },
NetworkViolation { module: String, host: String, port: u16 },
SuspiciousPattern { module: String, pattern: String },
}
impl SecurityMonitor {
pub fn new() -> (Self, mpsc::Receiver<SecurityEvent>) {
let (tx, rx) = mpsc::channel(1000);
(Self { tx }, rx)
}
pub async fn monitor_execution<F, T>(
&self,
module_name: &str,
time_limit: Duration,
f: F,
) -> Result<T>
where
F: FnOnce() -> Result<T>,
{
let start = Instant::now();
let module = module_name.to_string();
let tx = self.tx.clone();
// Set up monitoring
let monitor_handle = tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_millis(100));
loop {
interval.tick().await;
if start.elapsed() > time_limit {
let _ = tx.send(SecurityEvent::CpuTimeExceeded {
module: module.clone(),
duration: start.elapsed(),
limit: time_limit,
}).await;
break;
}
}
});
// Execute function
let result = f();
// Clean up monitor
monitor_handle.abort();
result
}
}
// Resource limiter using cgroups v2
pub struct CgroupLimiter {
cgroup_path: PathBuf,
}
impl CgroupLimiter {
pub fn new(name: &str) -> Result<Self> {
let cgroup_path = PathBuf::from("/sys/fs/cgroup").join(name);
std::fs::create_dir_all(&cgroup_path)?;
Ok(Self { cgroup_path })
}
pub fn set_memory_limit(&self, limit_bytes: u64) -> Result<()> {
let limit_path = self.cgroup_path.join("memory.max");
std::fs::write(limit_path, limit_bytes.to_string())?;
Ok(())
}
pub fn set_cpu_limit(&self, quota_us: u64, period_us: u64) -> Result<()> {
let max_path = self.cgroup_path.join("cpu.max");
std::fs::write(max_path, format!("{} {}", quota_us, period_us))?;
Ok(())
}
pub fn add_process(&self, pid: u32) -> Result<()> {
let procs_path = self.cgroup_path.join("cgroup.procs");
std::fs::write(procs_path, pid.to_string())?;
Ok(())
}
}
Performance Optimization
JIT Compilation Cache
// src/cache.rs - Module compilation cache
use wasmtime::Module;
use lru::LruCache;
use std::sync::Mutex;
pub struct CompilationCache {
cache: Mutex<LruCache<String, Module>>,
persistent_path: Option<PathBuf>,
}
impl CompilationCache {
pub fn new(capacity: usize, persistent_path: Option<PathBuf>) -> Self {
Self {
cache: Mutex::new(LruCache::new(capacity)),
persistent_path,
}
}
pub fn get_or_compile(
&self,
key: &str,
engine: &Engine,
wasm_bytes: &[u8],
) -> Result<Module> {
// Check in-memory cache
if let Some(module) = self.cache.lock().unwrap().get(key) {
return Ok(module.clone());
}
// Check persistent cache
if let Some(path) = &self.persistent_path {
let cache_file = path.join(format!("{}.compiled", key));
if cache_file.exists() {
let compiled_bytes = std::fs::read(&cache_file)?;
if let Ok(module) = unsafe { Module::deserialize(engine, &compiled_bytes) } {
self.cache.lock().unwrap().put(key.to_string(), module.clone());
return Ok(module);
}
}
}
// Compile module
let module = Module::new(engine, wasm_bytes)?;
// Store in cache
self.cache.lock().unwrap().put(key.to_string(), module.clone());
// Persist compiled module
if let Some(path) = &self.persistent_path {
let cache_file = path.join(format!("{}.compiled", key));
let compiled_bytes = module.serialize()?;
std::fs::write(cache_file, compiled_bytes)?;
}
Ok(module)
}
}
Performance Monitoring
#!/bin/bash
# monitor_wasm_performance.sh
# Monitor WASM runtime performance
echo "=== WASM Performance Monitoring ==="
# CPU usage by WASM processes
echo -e "\n1. CPU Usage:"
top -b -n 1 | grep -E "wasmtime|wasm" | head -10
# Memory usage
echo -e "\n2. Memory Usage:"
ps aux | grep -E "wasmtime|wasm" | awk '{sum+=$6} END {print "Total RSS: " sum/1024 " MB"}'
# Module execution times
echo -e "\n3. Module Execution Times:"
journalctl -u wasm-runtime --since "1 hour ago" | grep "execution_time" | \
awk '{print $NF}' | sort -n | \
awk '{
count++;
sum+=$1;
if(count==1) min=$1;
if(count==NR) max=$1;
} END {
print "Min: " min "ms";
print "Max: " max "ms";
print "Avg: " sum/count "ms";
}'
# JIT compilation stats
echo -e "\n4. JIT Compilation Stats:"
perf stat -e cycles,instructions,cache-misses \
wasmtime run --profile=jitdump test.wasm 2>&1 | grep -E "cycles|instructions|cache"
# Module cache hit rate
echo -e "\n5. Cache Statistics:"
redis-cli --raw HGETALL wasm:cache:stats 2>/dev/null || echo "Cache not available"
Integration with Container Ecosystems
WASM in Kubernetes
# wasm-runtime-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasm-runtime
namespace: wasm-platform
spec:
replicas: 3
selector:
matchLabels:
app: wasm-runtime
template:
metadata:
labels:
app: wasm-runtime
spec:
containers:
- name: wasmtime
image: wasmtime/wasmtime:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2000m"
volumeMounts:
- name: wasm-modules
mountPath: /modules
- name: wasm-cache
mountPath: /cache
env:
- name: WASMTIME_CACHE_CONFIG
value: "/cache/config.toml"
- name: RUST_LOG
value: "wasmtime=debug"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
volumes:
- name: wasm-modules
persistentVolumeClaim:
claimName: wasm-modules-pvc
- name: wasm-cache
emptyDir:
sizeLimit: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: wasm-runtime
namespace: wasm-platform
spec:
selector:
app: wasm-runtime
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
Edge Deployment with WASM
// src/edge.rs - Edge computing with WASM
use std::sync::Arc;
use tokio::time::{timeout, Duration};
pub struct EdgeRuntime {
runtime: Arc<WasmRuntime>,
cache: Arc<CompilationCache>,
config: EdgeConfig,
}
pub struct EdgeConfig {
pub max_concurrent_executions: usize,
pub execution_timeout: Duration,
pub cache_size: usize,
pub preload_modules: Vec<(String, String)>,
}
impl EdgeRuntime {
pub async fn new(config: EdgeConfig) -> Result<Self> {
let runtime_config = RuntimeConfig {
memory_limit: 128 * 1024 * 1024, // 128MB for edge
fuel_limit: Some(100_000_000),
epoch_deadline: Some(30_000), // 30 seconds
allowed_paths: vec!["/tmp/edge-wasm".to_string()],
env_vars: vec![],
};
let runtime = Arc::new(WasmRuntime::new(runtime_config)?);
let cache = Arc::new(CompilationCache::new(config.cache_size, None));
let edge = Self {
runtime,
cache,
config,
};
// Preload modules
for (name, version) in &config.preload_modules {
edge.preload_module(name, version).await?;
}
Ok(edge)
}
pub async fn execute_at_edge(
&self,
module_name: &str,
function: &str,
input: &[u8],
) -> Result<Vec<u8>> {
// Execute with timeout
timeout(
self.config.execution_timeout,
self.execute_internal(module_name, function, input)
).await?
}
async fn execute_internal(
&self,
module_name: &str,
function: &str,
input: &[u8],
) -> Result<Vec<u8>> {
// Implementation
todo!()
}
}
Monitoring and Observability
Prometheus Metrics
// src/metrics.rs - Prometheus metrics for WASM runtime
use prometheus::{
register_counter_vec, register_histogram_vec, register_gauge_vec,
CounterVec, HistogramVec, GaugeVec,
};
lazy_static! {
pub static ref MODULE_EXECUTIONS: CounterVec = register_counter_vec!(
"wasm_module_executions_total",
"Total number of WASM module executions",
&["module", "function", "status"]
).unwrap();
pub static ref EXECUTION_DURATION: HistogramVec = register_histogram_vec!(
"wasm_execution_duration_seconds",
"WASM execution duration in seconds",
&["module", "function"],
vec![0.001, 0.01, 0.1, 0.5, 1.0, 5.0, 10.0]
).unwrap();
pub static ref MEMORY_USAGE: GaugeVec = register_gauge_vec!(
"wasm_memory_usage_bytes",
"Current memory usage by WASM modules",
&["module"]
).unwrap();
pub static ref COMPILATION_TIME: HistogramVec = register_histogram_vec!(
"wasm_compilation_duration_seconds",
"WASM module compilation time",
&["module"],
vec![0.01, 0.1, 0.5, 1.0, 5.0]
).unwrap();
pub static ref CACHE_HITS: CounterVec = register_counter_vec!(
"wasm_cache_hits_total",
"Number of compilation cache hits",
&["cache_type"]
).unwrap();
}
Best Practices and Production Deployment
Deployment Checklist
# wasm-deployment-checklist.yaml
deployment_checklist:
security:
- [ ] Enable sandboxing for all modules
- [ ] Configure resource limits (memory, CPU, fuel)
- [ ] Set up capability-based security policies
- [ ] Enable execution monitoring and alerting
- [ ] Regular security audits of WASM modules
performance:
- [ ] Enable JIT compilation caching
- [ ] Configure appropriate memory limits
- [ ] Set up performance monitoring
- [ ] Enable SIMD and threads where appropriate
- [ ] Profile and optimize hot paths
operations:
- [ ] Set up module registry and versioning
- [ ] Implement health checks
- [ ] Configure logging and tracing
- [ ] Set up backup and recovery procedures
- [ ] Document module dependencies
integration:
- [ ] Test with existing infrastructure
- [ ] Set up CI/CD pipelines
- [ ] Configure monitoring dashboards
- [ ] Implement gradual rollout strategy
- [ ] Plan for backward compatibility
Conclusion
Building a WebAssembly runtime platform with Wasmtime on Rocky Linux provides a powerful, secure, and performant environment for running portable applications. The combination of strong sandboxing, near-native performance, and language independence makes WASM an ideal choice for edge computing, serverless functions, and plugin systems.
By following the architecture and best practices outlined in this guide, you can deploy a production-ready WASM platform that scales efficiently while maintaining security and performance requirements.