Skip to content

Migrate to v2 configuration files#

v2 is the current shape for buf.yaml and buf.gen.yaml. The Buf CLI continues to read v1 and v1beta1 configurations, so there’s no deadline; migrate when you want any of the v2-only features:

  • Workspace-level lint and breaking defaults, with per-module overrides.
  • Multi-module push: a single buf push covers every module in the workspace, in the right dependency order, against a single buf.lock.
  • Inputs declared in buf.gen.yaml, so buf generate works without a wrapping Makefile or shell script.
  • Simpler managed mode: top-level disable and override keys instead of options interwoven per file and per module.

v1beta1 configurations migrate the same way as v1; you don’t need to step through v1 first.

Run the migration tool#

buf config migrate discovers every buf.yaml, buf.gen.yaml, and buf.work.yaml under the current directory and rewrites them as a single v2 buf.yaml (and a v2 buf.gen.yaml if any v1 buf.gen.yaml was found) at the current directory. Run it from the workspace root:

$ buf config migrate

To preview the v2 files and directory layout before changing anything, print a diff:

$ buf config migrate --diff

The tool doesn’t auto-detect configuration files with non-standard names like buf.gen.go.yaml. For those, or to migrate parts of a repository at a time, target specific paths:

Migrate only buf.gen.yaml files at the specified paths
$ buf config migrate --buf-gen-yaml </paths/to/files>
Migrate only the modules at the specified paths
# Migrates buf.yaml and buf.lock files
$ buf config migrate --module </paths/to/modules>
Migrate only the workspaces at the specified paths
# Migrates buf.work.yaml, buf.yaml, and buf.lock files
$ buf config migrate --workspace </paths/to/workspaces>

After migration, run buf build and buf lint from the repository root to confirm the v2 config behaves the same.

Handle the new PACKAGE_NO_IMPORT_CYCLE rule#

v2 adds the PACKAGE_NO_IMPORT_CYCLE rule to the STANDARD lint category. The migration tool excludes the rule in the new buf.yaml so the migration itself doesn’t introduce new failures:

version: v2
lint:
  use:
    - STANDARD
  except:
    - PACKAGE_NO_IMPORT_CYCLE

Remove the except block and run buf lint. If the workspace passes, leave the rule on (delete the two lines). If it fails, leave the rule excluded until the import cycles can be addressed.

buf.yaml: modules and workspaces#

In v2, the workspace is the unit of local Protobuf work, whether it contains one module or many. A single buf.yaml at the workspace root replaces the buf.work.yaml plus per-module buf.yaml files of v1.

v1 layout#

v1 workspace directory structure
workspace_root
├── buf.work.yaml
├── proto
│   ├── foo
│   │   └── foo.proto
│   ├── bar
│   │   └── bar.proto
│   └── buf.yaml
└── vendor
    └── baz.proto
    └── buf.yaml
v1 proto/buf.yaml
version: v1
deps:
  - buf.build/googleapis/googleapis
v1 vendor/buf.yaml
version: v1
v1 buf.work.yaml
version: v1
directories:
    - proto
    - vendor

v2 layout#

v2 workspace directory structure
workspace_root
├── buf.yaml
├── proto
│   ├── foo
│   │   └── foo.proto
│   └── bar
│       └── bar.proto
└── vendor
    └── baz.proto
v2 buf.yaml
version: v2
modules:
  - path: proto
  - path: vendor
deps:
  - buf.build/googleapis/googleapis

For a single-module repository where the buf.yaml sits next to the .proto files, the only required change is the version key. With modules omitted, the Buf CLI treats the current directory as the single module:

version: v2
# Implicit if `modules` is omitted; including this block has no effect.
modules:
  - path: .

Multi-module push#

In multi-module workspaces, buf push now publishes every module in the workspace in dependency order against a single shared buf.lock. You no longer need to push modules one at a time after running buf dep update on each. Modules within the workspace can also depend on one another without an explicit declaration.

Workspace-level lint and breaking defaults#

v2 allows workspace-level lint and breaking blocks, which set defaults for every module. A module-level block on a specific module fully replaces the workspace-level rules for that module, rather than merging.

buf.yaml: module-level overrides of workspace lint rules
version: v2
breaking:
  use:
    - FILE
# By default, all modules in the workspace use the STANDARD lint rules.
lint:
  use:
    - STANDARD
modules:
  - path: proto
  - path: vendor
    breaking:
      use:
        - WIRE_JSON
    # The module under vendor/ uses the MINIMAL lint rules instead.
    lint:
      use:
        - MINIMAL

For everything else, see Modules and workspaces and the v2 buf.yaml reference.

buf.gen.yaml: plugins, inputs, and managed mode#

Plugin type keys#

Plugins still live under a top-level plugins list, but the kind of plugin is now an explicit key: remote, local, or protoc_builtin. The two configurations below are equivalent:

v1 buf.gen.yaml
version: v1
plugins:
    # Remote plugin on the BSR
    - plugin: buf.build/protocolbuffers/java
      out: gen/proto
    # Local binary plugin in ${PATH}
    - plugin: validate
      out: gen/proto
    # protoc built-in plugin for C++ (note lack of "protoc-gen-" prefix)
    - plugin: cpp
      out: gen/proto
v2 buf.gen.yaml
version: v2
plugins:
    # Remote plugin on the BSR
    - remote: buf.build/protocolbuffers/java
      out: gen/proto
    # Local binary plugin in ${PATH}
    - local: protoc-gen-validate
      out: gen/proto
    # protoc built-in plugin for C++ (note lack of "protoc-gen-" prefix)
    - protoc_builtin: cpp
      out: gen/proto

Inputs declared in config#

v2 buf.gen.yaml accepts an inputs list, so a single buf generate covers an entire workspace. The configuration accepts modules, local directories, individual .proto files, Git repositories, tarballs and zip archives, and Buf images. Anything passed on the command line still overrides what’s in the file.

For this layout:

Local workspace example
workspace_root
├── buf.gen.yaml
├── buf.yaml
├── proto
│   ├── foo
│   │   └── foo.proto
│   └── bar
│       └── bar.proto
└── baz
    └── baz.proto

v1 requires running buf generate once per input directory:

v1: must run the command twice
$ buf generate proto
$ buf generate baz

v2 declares both inputs in buf.gen.yaml and runs once:

v2 buf.gen.yaml with inputs
version: v2
inputs:
    - directory: proto
    - directory: baz
v2: generates code for all inputs in buf.gen.yaml
$ buf generate

Managed mode: disable and override#

Managed mode now uses two top-level keys, disable and override, instead of weaving overrides into per-file-option blocks. Coverage now extends to field options as well as file options, and entries can scope down to individual fields. A file option without a default behavior is left at the Protobuf default unless an override rule sets it.

The following two configurations are equivalent:

v1 buf.gen.yaml managed mode
version: v1
managed:
    enabled: true
    optimize_for: CODE_SIZE
    go_package_prefix:
        default: github.com/acme/weather/private/gen/proto/go
        except:
            - buf.build/googleapis/googleapis
        override:
            buf.build/acme/weather: github.com/acme/weather/gen/proto/go
    override:
        JAVA_PACKAGE:
            acme/weather/v1/weather.proto: "org"
v2 buf.gen.yaml managed mode
version: v2
managed:
  enabled: true
  disable:
    # Disables all go_package changes for this module only
    - file_option: go_package
      module: buf.build/googleapis/googleapis
  override:
    - file_option: optimize_for
      value: CODE_SIZE
    # Sets default go_package_prefix for all inputs
    - file_option: go_package_prefix
      value: github.com/acme/weather/private/gen/proto/go
    # Overrides default go_package_prefix for this module only
    - file_option: go_package_prefix
      module: buf.build/acme/weather
      value: github.com/acme/weather/gen/proto/go
    # File options with prefix and suffix can now specify defaults directly
    - file_option: java_package
      path: acme/weather/v1/weather.proto
      value: org

For everything else, see code generation overview, managed mode, and the v2 buf.gen.yaml reference.

buf mod command moves#

Because buf.yaml is now the configuration file for an entire workspace, the buf mod subcommands moved out of buf mod into namespaces that match what they actually act on:

v1 command v2 command
buf mod init buf config init
buf mod prune buf dep prune
buf mod update buf dep update
buf mod ls-breaking-rules buf config ls-breaking-rules
buf mod ls-lint-rules buf config ls-lint-rules
buf mod {clear-cache,cc} buf registry cc

The buf mod subcommands still work and print a deprecation warning to stderr:

$ buf mod update
Command "update" is deprecated, use "buf dep update" instead. However, "buf mod update" will continue to work.

Further reading#