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 plugin | Component host plugin | Service | |
|---|---|---|---|
| Form | Rust trait impl linked into the host binary | Wasm component deployed into the host | Wasm component shipped with the workload |
| Author language | Rust | Any language that targets wasm32-wasip2/p3 | Any language that targets wasm32-wasip2/p3 |
| Decision point | Host build time | Host runtime | Workload deploy time |
| Scope | All workloads on the host | All workloads on the host | One workload |
| Execution | In-process (added to the Wasmtime linker) | Pinned Wasm instance in the host, called across a store boundary | Wasm instance inside the workload |
| Shared state across workloads | Yes—e.g. one connection pool or cache | Yes—via the pinned instance | No—sandboxed per workload |
| Direct host resources (raw sockets, filesystem paths, hardware) | Yes | No—runs inside the sandbox | No—runs inside the sandbox |
| TCP listen() | No | No | Yes, on loopback and unspecified addresses |
| Auto-restart on crash | N/A | Yes (bounded restart budget) | Yes |
| Declared by workload as | host_interfaces: [...] | host_interfaces: [...] | service: { ... } |
| Operator-facing toggle | Cargo features on the host crate | host-component-plugins feature + plugin deploy | Manifest field on the workload |
Decision guide
The choice divides on two hard filters and one soft one:
- 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.
- 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.
- 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.
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 Secrets → Native 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 broker → Component 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 workload → Service. 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 driver → Native 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 components → Service.
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
- Plugins overview—native and component host plugins, and the registration API.
- Trigger services—the execution model shared by services and component host plugins.
- Services overview—the service model, TCP isolation, and export requirements.
- Creating Host Plugins—the
HostPlugintrait reference for native plugin authors. - Creating component host plugins—the authoring guide for component-based plugins.
- Creating Services—the developer-guide walkthrough for service authors.