Skip to main content
Version: v2

Plugin or Service?

This recipe gives you a framework for choosing between three ways of providing capabilities to a wasmCloud application: extending the host with a native plugin, extending the host with a component host plugin, or shipping a service inside the workload. The three are not interchangeable—each has hard constraints that rule it out in some situations, and softer trade-offs in the rest.

A quick introduction

A native host plugin is a Rust implementation of the HostPlugin trait, compiled into the host binary. It is native Rust code that is available to and executed by components via calling into the host.

More concretely, when workloads are bound, the runtime adds the plugin's WIT imports directly to the wasmtime linker for each component or service in the workload, so calls from guest code dispatch in-process. wash-runtime ships with built-in native plugins for interfaces like wasi:keyvalue, wasi:blobstore, wasi:config, wasi:logging, wasi:otel, wasmcloud:messaging, and more. See the Native host plugins section.

A component host plugin is a WebAssembly component that provides a host capability to workloads. Rather than compiling into the host binary, it is declared in host configuration and loaded at host startup, serving capability calls from other workloads through a capability ingress, running as a trigger service with one long-lived pinned instance. See the Component host plugins section.

A wasmCloud service is a Wasm component that runs for the lifetime of a single workload, deployed as part of a WorkloadDeployment. Unlike other components in a WorkloadDeployment, services receive the wasi:sockets TCP bind permission, can listen on loopback and unspecified addresses to interact with other components in the workload, and are automatically restarted if they crash. A service exports either wasi:cli/run or exactly one WIT interface so the runtime has an unambiguous entry point. See the Services overview.

Comparison

Native pluginComponent host pluginService
FormRust trait impl linked into the host binaryWasm component deployed into the hostWasm component shipped with the workload
Author languageRustAny language that targets wasm32-wasip2/p3Any language that targets wasm32-wasip2/p3
Decision pointHost build timeHost runtimeWorkload deploy time
ScopeAll workloads on the hostAll workloads on the hostOne workload
ExecutionIn-process (added to the Wasmtime linker)Pinned Wasm instance in the host, called across a store boundaryWasm instance inside the workload
Shared state across workloadsYes—e.g. one connection pool or cacheYes—via the pinned instanceNo—sandboxed per workload
Direct host resources (raw sockets, filesystem paths, hardware)YesNo—runs inside the sandboxNo—runs inside the sandbox
TCP listen()NoNoYes, on loopback and unspecified addresses
Auto-restart on crashN/AYes (bounded restart budget)Yes
Declared by workload ashost_interfaces: [...]host_interfaces: [...]service: { ... }
Operator-facing toggleCargo features on the host cratehost-component-plugins feature + plugin deployManifest field on the workload

Decision guide

The choice divides on two hard filters and one soft one:

  1. Does the capability need to listen on a TCP port in the workload?Service. Plugins of either kind serve capability calls; they don't bind TCP listeners.
  2. Does the capability need direct host resources (raw sockets, filesystem paths, hardware, or host-level privileges)?Native plugin. Component host plugins run inside the Wasm sandbox and cannot reach those resources.
  3. Should the capability be shared across workloads on the host?Plugin (native or component). If it should be per-workload, → Service.

If both plugin kinds are viable, prefer the component host plugin unless you have a specific reason to link into the host binary. Component plugins ship, version, and sandbox like any other component, and you can iterate on them without rebuilding the host.

Decision tree for choosing a plugin or a service

note

The decision-tree image above reflects the earlier two-way native-plugin-vs.-service split; the three-way guidance in the list above supersedes it while the diagram is updated.

When to choose a native plugin

Pick a native plugin when:

  • The capability needs direct host resources—raw sockets, filesystem paths, specialized hardware, or anything that requires running outside the Wasm sandbox.
  • The capability is infrastructure—storage, telemetry, secrets, a custom database driver—that behaves the same across every workload, and it's low-level enough that a Wasm implementation isn't practical.
  • You control the host build and can roll a new host image to ship any required changes.

When to choose a component host plugin

Pick a component host plugin when:

  • The capability is infrastructure (should behave the same across workloads) and can be implemented in Wasm—no direct host-resource access required.
  • You want to ship, version, or sandbox the capability using the same primitives as any other component (OCI registry, per-instance restart budget, Wasm sandbox).
  • You want to iterate on the capability independently of the host binary.
  • The implementation isn't Rust, and it still needs to be shared across workloads (a native plugin would require Rust; a service would scope it to one workload).

When to choose a service

Pick a service when:

  • The work is specific to one application and shouldn't bleed across deployments—a per-tenant connection, a request batcher, an in-memory cache that only one workload uses.
  • You need to listen on a TCP port inside the workload, whether as a real protocol server or as a way to bridge component invocations into a streaming connection.
  • The behavior ships with the application artifact, not with the platform. Updating it means redeploying the workload, not rebuilding or redeploying the plugin.

Best-practice examples

Per-app configuration from Kubernetes ConfigMaps and SecretsNative plugin. The built-in wasi:config plugin reads runtime configuration uniformly for every workload on the host. Platform-level concern, language-agnostic, single code path. See the config-injection recipe.

A custom messaging backend targeting an internal brokerComponent host plugin. The capability should be shared across workloads (one broker connection serves many), but the implementation can be pure Wasm. Building it as a component lets you ship it via OCI, iterate on it without rebuilding the host, and keep it sandboxed. See Creating component host plugins.

Cron-driven invocation of components in the same workloadService. Application logic that periodically calls a component interface, lives with the workload, and benefits from automatic restart. See the cron-style service example in the Services overview.

Connecting to a proprietary database with a Rust-only driverNative plugin. Multiple workloads share one driver and one connection pool, and the driver isn't available in Wasm. Build a custom native plugin against the HostPlugin trait. Components stay portable—they import a WIT interface, not a Rust SDK. See Creating Host Plugins.

A TCP protocol adapter that translates between an external system and your componentsService. Listening on a TCP port inside the workload is a hard requirement here. A service can bind to 127.0.0.1, accept incoming connections, and call into companion components over WIT. Plugins of either kind cannot listen on ports.

Keep reading