# protoc-gen-doc: turning .proto comments into HTML, Markdown, DocBook and JSON

> A protoc plugin that reads leading and trailing comments out of your .proto files and renders them as documentation. It is aimed at teams whose protobuf schemas have outgrown a wiki page, and it is best installed as the published Docker image.

**pseudomuto/protoc-gen-doc** — Documentation generator plugin for Google Protocol Buffers

- Repository: https://github.com/pseudomuto/protoc-gen-doc
- Stars: 2,841 · Forks: 492
- Language: Go
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/pseudomuto-protoc-gen-doc

## The gap between a .proto file and a page a human can read

A .proto file is a contract with comments attached. The comments explain why a field exists, what units it carries, which values are deprecated, and what a service method does when it fails. None of that reaches the person writing the client, because protoc emits descriptors and generated code, not prose. protoc-gen-doc fills that gap. It is a protoc plugin, so it runs inside the normal compiler invocation and turns the comments already sitting in your schemas into HTML, JSON, DocBook or Markdown. The audience is anyone who owns a proto repository and has to publish something readable from it: platform teams maintaining shared message definitions, API groups that hand schemas to external consumers, and reviewers who want the generated documentation to sit next to the code in the same pull request. If your protos have no comments, the tool has nothing to work with and the output will be a structured list of names and types.

## How the plugin reads comments and writes four formats

The mechanism is a protoc plugin, which means protoc parses the .proto files, builds a CodeGeneratorRequest, and pipes it to the protoc-gen-doc binary over stdin. The binary writes a CodeGeneratorResponse back. The repository layout reflects this: cmd/ holds the entry point, plugin.go handles the plugin protocol, and renderer.go and template.go do the rendering. The parsing of comments is delegated to github.com/pseudomuto/protokit, which is listed as a direct dependency in go.mod, and templating uses Go's text/template plus github.com/Masterminds/sprig/v3 for the function set. The README states that messages, fields, services and their methods, enums and their values, extensions and files can all be documented. Comments come in two forms. Leading comments can appear anywhere and are attached to the declaration that follows them; file-level comments must be leading comments on the syntax directive. Trailing comments work on fields, service methods, enum values and extensions, so a line like DEFAULT = 0; // the default value is captured. A comment prefixed with @exclude is kept in the source but dropped from the output, which matters when a note is meant for maintainers rather than consumers. Format selection happens at invocation time through --doc_opt, and the same parsed model feeds all four built-in outputs, so switching from HTML to Markdown is a flag change rather than a second pipeline.

## Installing protoc-gen-doc and generating your first page

The README calls the Docker image the recommended route because it bundles protoc, protobuf-dev and the plugin itself. The image declares two volumes, /out for the generated documentation and /protos for the input files, and its default command is --doc_opt=html,index.html, so a bare run produces HTML for every .proto file found in /protos. Pull it first.

```bash
docker pull pseudomuto/protoc-gen-doc
```

Then mount your own directories. The paths on the left are host paths; the paths referenced inside the command must be container paths.

```bash
docker run --rm \
  -v $(pwd)/examples/doc:/out \
  -v $(pwd)/examples/proto:/protos \
  pseudomuto/protoc-gen-doc
```

After that run, /out/index.html on the host holds the rendered documentation. To get Markdown instead, pass --doc_opt with the format and the output filename.

```bash
docker run --rm \
  -v $(pwd)/examples/doc:/out \
  -v $(pwd)/examples/proto:/protos \
  pseudomuto/protoc-gen-doc --doc_opt=markdown,docs.md
```

If you already have protoc locally, install the plugin with go get and let protoc find it on PATH. The README gives this form:

```bash
go get --tool github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest
```

Pre-built release binaries are also published, and the plugin is available on Maven Central with a Gradle example in examples/gradle. With the binary on PATH, a local invocation looks like this:

```bash
protoc --doc_out=./doc --doc_opt=html,index.html proto/*.proto
```

The --doc_opt value has a fixed shape: --doc_opt=<FORMAT>|<TEMPLATE_FILENAME>,<OUT_FILENAME>[,default|source_relative]. The format is one of docbook, html, markdown or json, or a path to a Go template file. Adding source_relative writes the output beside the input file instead of into --doc_out. There is a third segment for extra options, separated by a second colon, and the only documented option is camel_case_fields=true, which emits field names in lowerCamelCase; the default is false.

## Excluding imports, single files and the Docker wildcard trap

Real proto trees pull in google/api/annotations.proto, validate/validate.proto and similar third-party files, and you usually do not want those rendered alongside your own messages. Exclusions are appended to the format segment after a colon, as comma-separated path patterns. The README example is --doc_opt=html,index.html:google/*,third_party/*, and the Makefile uses the same mechanism with :Ignore* to keep fixture protos out of the published examples. The Docker image accepts the exclusion patterns as its second option, also colon-delimited, which is why a run can look like --doc_opt=:google/*,somepath/* with an empty format segment. Two constraints are worth knowing before you script this. First, the README warns that wildcard expansion in Docker does not work the way it does in a shell: you cannot pass protos/*.proto in the file list, and the documented workaround is to pass no files at all, which makes the container generate docs for protos/*.proto, or to mount different volumes. Second, paths in the Docker form must be container paths, not host paths, which is stated explicitly in the README. For a single file, pass the filename after the options, for example Booking.proto, and list more files after it if needed. When the plugin binary is not on PATH, protoc's --plugin flag points at it directly, as shown in the README with --plugin=protoc-gen-doc=./protoc-gen-doc.

## Custom templates, sprig functions and stylesheet.css

The built-in formats are a starting point, not the whole product. Passing a path instead of a format name uses that file as a Go template, so --doc_opt=/path/to/template.tmpl,index.txt renders through your own template. The examples/templates directory in the repository contains an asciidoc.tmpl used by the Makefile to produce example.txt, which is the clearest evidence that the template surface is broad enough to emit a format the tool does not ship. Template arguments and functions are documented on the project wiki rather than in the README, a split worth noting because the wiki is a separate artifact and the README links to it as Custom Templates. For HTML specifically, the README says that placing a stylesheet.css file next to the output is enough to change the look, which avoids forking a template just to adjust colours or spacing. The dependency on sprig means the template function set is larger than plain text/template, and the dependency on protokit means the comment model is not maintained inside this repository.

## Where protoc-gen-doc stops being the right tool

The plugin documents what is in the .proto files. It does not describe HTTP bindings, request bodies or status codes unless those live in comments, and it does not produce an OpenAPI document. If your consumers need a machine-readable REST contract, protoc-gen-openapiv2 solves a different problem and the two are not substitutes. There is also a maintenance signal to weigh. The repository is not archived, and the last push to master was on 2026-07-21, but the most recent release listed is v1.5.1 from 2022-02-18, so anyone installing a tagged build is installing code that predates the current dependency set in go.mod, which pins google.golang.org/protobuf v1.36.11 and Go 1.26.4. Building from master and installing a release are therefore different propositions, and the README does not document a rollback path if a generated page regresses. Finally, the Docker image is built on alpine:3.15.0, which is old enough that you should check it against your own base-image policy before adopting it in a pipeline. None of these are reasons to avoid the tool for its stated job; they are reasons to pin deliberately and to know which artifact you are running.

## What it costs to keep running

Running costs are low because there is no service to operate. The plugin is a binary that protoc invokes, or a container that runs once and exits. The upgrade surface is the protoc invocation itself: --doc_opt, the exclusion patterns, and the optional camel_case_fields setting. If you use built-in formats, upgrades are mostly invisible. If you use a custom template, the template is coupled to the argument shape documented on the wiki, and that is where breakage would surface first. The MIT licence is permissive and the LICENSE.md file sits at the repository root, but the repository also vendors third-party proto trees under thirdparty/ and the Makefile mounts them as include paths, so if you copy that setup, check the licences of those vendored files separately. That is a description of what is in the repository, not legal advice; your own counsel decides what your distribution requires.

## Conclusion

Adopt protoc-gen-doc if your proto files already carry comments and you want those comments rendered as HTML, Markdown, DocBook or JSON without a second source of truth. Do not adopt it if you need a hosted documentation site, an OpenAPI specification, or a generator that is updated on a frequent release cadence: the newest release listed in the repository is v1.5.1 from 2022-02-18, even though the last push to master was on 2026-07-21. Before you commit, run the Docker image against one real proto directory and check three things: that the leading comment on the syntax line becomes the file-level description, that @exclude strips the comments you expected it to strip, and that any third-party imports you rely on resolve through the mounted include paths rather than failing the protoc invocation.

## FAQ

### How do I install protoc-gen-doc?

The README recommends the Docker image, pulled with docker pull pseudomuto/protoc-gen-doc, because it bundles protoc and the plugin. Alternatively you can install it with go get --tool github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest, download a pre-built release for your platform, or use the Maven Central artifact described in the Gradle example.

### What does protoc-gen-doc generate from a proto file?

It generates documentation from the comments in your .proto files in HTML, JSON, DocBook or Markdown, selected through the --doc_opt option. It supports proto2 and proto3, including both in the same context, and can render through a custom Go template instead of a built-in format.

### Can I exclude some proto files or comments from the generated documentation?

Yes. Files can be excluded with comma-separated path patterns appended after a colon in --doc_opt, for example html,index.html:google/*,third_party/*. Individual comments can be dropped by prefixing them with @exclude, which keeps the comment in the source but removes it from the output.

### Does protoc-gen-doc work on Windows?

The README states that pre-built releases are available for download per platform, and the Docker image is the recommended route on any host that runs Docker. The README does not give Windows-specific instructions beyond those two options.

## Sources

- [Issues](https://github.com/pseudomuto/protoc-gen-doc/issues)
- [License: MIT](https://github.com/pseudomuto/protoc-gen-doc/blob/master/LICENSE)
- [pseudomuto/protoc-gen-doc on GitHub](https://github.com/pseudomuto/protoc-gen-doc)
- [README](https://github.com/pseudomuto/protoc-gen-doc/blob/master/README.md)
- [Releases](https://github.com/pseudomuto/protoc-gen-doc/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pseudomuto-protoc-gen-doc
