# hashicorp/go-plugin: an RPC plugin system for Go hosts and multi-language plugins

> hashicorp/go-plugin launches plugins as subprocesses and talks to them over net/rpc or gRPC, so a panic in a plugin cannot take down the host. It is the right tool when the host and plugin ship separately; it is the wrong tool for anything that has to run across a real network.

**hashicorp/go-plugin** — Golang plugin system over RPC.

- Repository: https://github.com/hashicorp/go-plugin
- Stars: 6,095 · Forks: 514
- Language: Go
- License: MPL-2.0
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/hashicorp-go-plugin

## What hashicorp/go-plugin is for, and who ends up using it

The problem is extension without recompilation. A tool wants third parties to add behaviour, but it does not want to link their code into its own binary, rebuild when they change, or trust them with its address space. hashicorp/go-plugin solves that by turning each plugin into a separate process and putting an RPC boundary between it and the host. The README describes the system as one that has been in use by HashiCorp tooling for over four years, and names Packer, Terraform, Nomad, Vault, Boundary and Waypoint as users.

The audience is therefore narrow and specific. You are writing a Go program that needs to load code you did not compile in. You are willing to define interfaces, implement an RPC client and server for each one, and manage plugin binaries as artifacts. In exchange, the plugin gets its own process, its own crash domain, and the freedom to be written in a language other than Go if you choose the gRPC path. The README frames the benefit directly: a panic in a plugin does not panic the plugin user. For a tool like Terraform, where plugins are providers written by many different parties, that isolation is the whole point.

If you are looking for a way to call a function in a shared object loaded into your own process, this is not that project. The README has a section titled What About Shared Libraries that contrasts the two approaches, noting that when HashiCorp started using plugins in late 2012 and early 2013, plugins over RPC were the only option because Go did not support dynamic library loading.

## Subprocesses, yamux and gRPC: how the plugin boundary actually works

The architecture is stated in one sentence in the README: the system works by launching subprocesses and communicating over RPC, using either standard net/rpc or gRPC. A single connection is made between any plugin and the host process. That single connection is the constraint everything else is built around.

For net/rpc plugins, additional connections are multiplexed on top of that one link using hashicorp/yamux, which appears in go.mod as a direct dependency. For gRPC plugins, HTTP2 handles the multiplexing instead, so yamux is not in that path. This is why the library can support complex arguments and return values such as interfaces and io.Reader/Writer: the MuxBroker gives you a way to open new connections between client and server to serve additional interfaces or move raw data. Bidirectional communication falls out of the same mechanism, so the host can hand an interface implementation to the plugin and the plugin can call back into the host.

Process management is the other half. Plugins are subprocesses, and the README notes that they can keep using stdout and stderr, with output mirrored back to the host, which can control which io.Writer those streams go to. TTY preservation goes further: the plugin subprocess is connected to the identical stdin file descriptor as the host, so a plugin that executes ssh looks and behaves normally to the end user despite the extra process and the RPC in between. Logging is handled similarly. Plugins that use the standard log package have their output forwarded to the host and prefixed with the path to the plugin binary; if the host uses hclog, the log data is structured, and a plugin that also uses hclog produces structured records on the host side too.

## Installing the library and running the basic example

There is no installer and no binary to download. hashicorp/go-plugin is a Go module, so you add it to a module with go get and then build the host and plugin programs yourself. The module path in go.mod is github.com/hashicorp/go-plugin, and the repository's go.mod declares go 1.25.0, so a toolchain at least that new is required.

```bash
go get github.com/hashicorp/go-plugin
```

The README points at the examples/ directory for working code, and the repository contains examples/basic, examples/bidirectional, examples/grpc, examples/negotiated and examples/streaming. The basic example is the shortest path to a running plugin, and the Makefile shows the test target building fixtures under internal/cmdrunner/testdata before running go test with the race detector.

```bash
make test-deps
make test
```

The usage sequence the README gives is five steps. Choose the interfaces you want to expose. For each interface, implement a client and a server that communicate over net/rpc or gRPC or both. Create a Plugin implementation that knows how to construct the RPC client and server for a given plugin type. Plugin authors call plugin.Serve from main. Plugin users use plugin.Client to launch the subprocess and request an interface implementation over RPC. The README is candid that step two, writing the client and server for each interface, is the most tedious and time consuming part, though it adds that examples exist in the examples/ directory and across HashiCorp's other open source projects.

If you would rather write the plugin in a language other than Go, the gRPC path is the one to take. The README states that gRPC-based plugins can be written in any language, and the repository carries buf.yaml and buf.gen.yaml for generating the protobuf code.

## The local-network constraint and the cost of the RPC layer

The most important limitation is stated without hedging in the README. The plugin system is over RPC, but it is currently only designed to work over a local reliable network. Plugins over a real network are not supported and will lead to unexpected behavior. That rules out the obvious idea of running a plugin on another machine and reaching it over TCP. If your architecture needs plugins as remote services, this library is the wrong tool, and the isolation you wanted has to come from somewhere else.

The second cost is boilerplate. Every interface you expose needs a client and a server implementation, and the README calls that step the most tedious and time consuming one. This is not a system where you annotate a function and get a plugin. You are writing the marshalling boundary by hand, or generating it, for each interface. Small hosts with one or two extension points pay a disproportionate price for that.

Versioning is deliberately coarse. The README describes a very basic protocol version that can be incremented to invalidate previous plugins, useful when interface signatures change or protocol-level changes are necessary, with a human friendly error message shown when a version is incompatible. That is a blunt instrument: it invalidates old plugins wholesale rather than expressing compatibility ranges. The README's own roadmap lists semantic versioning as future work, where plugins would implement a semantic version and the host would get a system for constraining versions, in addition to the protocol version already present. Until that lands, you manage compatibility yourself.

Security is bounded by the host. Plugins can be verified with an expected checksum and RPC traffic can be configured to use TLS, and the repository includes mtls.go for that, but the README is explicit that the host process must be properly secured to protect this configuration. The plugin also only has access to the interfaces and arguments given to it, not the host's entire memory space, which is a real reduction in blast radius rather than a sandbox.

## go-plugin compared with Go's own plugin package

The alternative most Go developers reach for first is the standard library plugin package, which loads a shared object into the running process. The difference in approach is the process boundary. With the standard library, the plugin is a .so file built with the same toolchain and loaded into your address space; a panic there can take down the host, and the plugin must be written in Go. There is no RPC, no serialization, and no subprocess to manage, which makes it simpler when it fits.

hashicorp/go-plugin trades that simplicity for isolation and language freedom. The plugin runs as a subprocess, so its crashes are its own, and with gRPC it can be written in almost any major language. It also gains the operational features that come with a process boundary: stdout and stderr mirroring, log forwarding, TTY preservation, and the ability to reattach to a running plugin so the host can be upgraded while the plugin keeps running, which the README describes as requiring the host and plugin to know this is possible and to daemonize properly, with NewClient taking a ReattachConfig.

There is a third option worth naming: writing your own subprocess protocol. That is what go-plugin is underneath, and it is a reasonable choice if you need remote plugins, since go-plugin explicitly does not support them. The cost is that you rebuild the multiplexing, the handshake, the logging bridge and the reattach logic yourself. The library's value is that those parts are already solved for the local case, and the README's claim that the system has been used on millions of machines across many different projects is the reason to prefer it over a fresh implementation.

## Conclusion

Adopt hashicorp/go-plugin if you control a Go host process and need plugins that ship as separate binaries, possibly written in another language, without sharing the host's memory space. Do not adopt it if your plugins must run on remote machines: the README states plainly that plugins over a real network are not supported and will lead to unexpected behavior. Before committing, read examples/basic and examples/grpc, confirm the protocol version constant in your host matches the one your plugins serve, and check whether you need the semantic versioning that the roadmap still lists as future work.

## FAQ

### What is a go plugin?

In this project, a plugin is a Go interface implementation that runs in a separate process. The host launches it as a subprocess and communicates with it over net/rpc or gRPC, so a panic in the plugin does not panic the host.

### Can hashicorp/go-plugin run plugins over a real network?

No. The README states that the system is currently only designed to work over a local reliable network, and that plugins over a real network are not supported and will lead to unexpected behavior.

### Can I write a hashicorp/go-plugin plugin in a language other than Go?

Yes, through gRPC. The README says the library supports serving plugins via gRPC, and that gRPC-based plugins enable plugins to be written in any language. The repository includes buf.yaml and buf.gen.yaml for protobuf generation.

### What Go version does hashicorp/go-plugin require?

The repository's go.mod declares go 1.25.0, so a toolchain at least that new is needed to build against the current main branch.

## Sources

- [hashicorp/go-plugin on GitHub](https://github.com/hashicorp/go-plugin)
- [Issues](https://github.com/hashicorp/go-plugin/issues)
- [License: MPL-2.0](https://github.com/hashicorp/go-plugin/blob/main/LICENSE)
- [README](https://github.com/hashicorp/go-plugin/blob/main/README.md)
- [Releases](https://github.com/hashicorp/go-plugin/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/hashicorp-go-plugin
