All public plugins on the BSR are maintained by Buf for security purposes—we want to make sure that the code generators everyone uses are verified by us. However, organizations often write custom Protobuf plugins to generate logic specific to their business. Custom plugins are available on our Teams, Pro, and Enterprise plans—reach out if you're interested in adding this feature.

  • Teams: Can upload unlimited custom plugins, but they must be private and limited to members of the organization
  • Pro and Enterprise: Can upload unlimited custom plugins, which can be public or private within your private BSR instance:
    • Public—All user accounts with access to the instance will have access to the plugin
    • Private—Only user accounts who are members of the organization that owns a plugin will have access to the plugin

The instructions below cover how to build, package, and push a custom plugin.

Custom plugins are not available as remote packages—use them as remote plugins to generate code locally instead.


  • A minimum Buf CLI version of v1.12.0 or higher.
  • Docker must be installed and running.
  • A BSR organization must exist, and the user pushing the plugin must have at least an Admin role.

An organization can be created through the BSR UI (see Manage organizations) or the Buf CLI (see the buf beta registry command reference).

  • Public BSR (Teams plan):

    buf beta registry organization create <{organization_name}>

  • Private BSR (Pro and Enterprise plan):

    buf beta registry organization create <{private_bsr_server}/{organization_name}>

Creating a custom plugin

The following are the steps we'll cover to build and push a custom plugin:

  1. Build Docker image (optional)
  2. Prepare buf.plugin.yaml file
  3. Push the plugin

Build a Docker image

If you already have a Docker image built with a Protobuf plugin that accepts a CodeGeneratorRequest from standard input and writes a CodeGeneratorResponse to standard output, then you can skip this step.

We'll use a Go-based plugin (protoc-gen-go-json) as an example. If you have a more advanced setup and need help packaging a plugin, don't hesitate to get in touch.

# syntax=docker/dockerfile:1.4
FROM golang:1.19-bullseye AS builder
    go install -ldflags "-s -w" -trimpath

# When building a Docker image on a host that does not match linux/amd64 (such as an M1),
# go install will put the binary in $GOPATH/bin/$GOOS_$GOARCH/. The mv command copies
# the binary to /go/bin so subsequent steps do not fail when copying from the builder.
RUN mv /go/bin/linux_amd64/* /go/bin || true

FROM scratch
COPY --from=builder --link /etc/passwd /etc/passwd
COPY --from=builder /go/bin/ /
USER nobody
ENTRYPOINT [ "/protoc-gen-go-json" ]
  • Make sure the OS/arch targets linux/amd64 (hard coded above). This is important, especially if you're building Docker images on an ARM-based machine, such as an Apple M1 computer.
  • If possible, use minimal images such as scratch or distroless and leverage multi-stage builds.
  • Set a non-root USER, such as nobody, and make sure that user exists.

Once you've prepared the Dockerfile, build and tag the image:

docker build --platform linux/amd64 -t .

Prepare buf.plugin.yaml

At a minimum, the buf.plugin.yaml file defines the plugin name and plugin version.

version: v1
plugin_version: v1.1.0
  • The version key is the YAML configuration version, always set to v1.
  • The plugin_version key has the format v{semver}—the v prefix is required and the version must be valid semantic versioning.
  • The name key format is {private_bsr_server}/{organization_name}/{plugin_name}

There is additional metadata that may be captured by the buf.plugin.yaml configuration file, such as the plugin source URL, the output language, description, dependencies, etc. See the buf.plugin.yaml file documentation.

Push plugin to the BSR

Make sure you have authenticated to the BSR.

Once you have a Docker image and a buf.plugin.yaml file, run the following command:

buf beta registry plugin push \
    --visibility [public,private] \

The --image flag is referencing an image built in an earlier step, but this can also be an image from an external source, such as Docker Hub or Quay. Make sure to docker pull the image so it is available locally.

Setting the visibility to private makes the plugin accessible to organization members only.

A successful push outputs details about the plugin, and the plugin is immediately available in the Plugins section within the organization.

Owner  Name     Version  Revision
acme   go-json  v1.1.0   1

Delete a custom plugin

You can delete a custom plugin with the Buf CLI by passing a plugin reference or a plugin reference and version.

If the version is omitted, then all versions for that plugin will be deleted.

# Delete all versions:
buf beta registry plugin delete

# Delete specific version:
buf beta registry plugin delete