docker-protoc: generating protobuf and gRPC code without installing a toolchain
Docker images for generating protocol buffer definitions
At a glance
- What is it?
- docker-protoc publishes container images that wrap protoc, prototool and grpc_cli so a team can generate protobuf bindings for Go, Java, Ruby, C#, node and the web without managing local tool versions. The images bundle the Google API protos and the well-known types, and the tag scheme is version-numbered against a gRPC release.
- Who is it for?
- docker-protoc is worth adopting if your team has more than one person generating protobuf bindings, because the whole class of problems where a developer's protoc version does not match the one in CI disappears when the generator is a container, and the bundled Google API protos remove a second source of drift.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 135 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem is version drift, not the compiler
docker-protoc is described as a set of support images that wrap protoc, prototool and grpc_cli commands with gRPC support in a variety of languages, removing the need to install and manage these commands locally.
That framing is accurate about what the project is for. The protoc compiler itself is not the hard part, installing it is, and more precisely keeping it consistent across a laptop, a CI runner and a colleague's machine is. A schema compiles locally and then fails in CI because the compiler versions differ, and the version that matters is not only protoc but the set of language plugins, each of which is a separate binary with its own release cadence.
The mechanism the images use is deliberately minimal. The README says it relies on setting a simple volume to the container, usually mapping the current directory to /defs, and specifying the file and the language you want to generate. Everything else about the invocation is ordinary protoc, so the tool inside the container is the same tool you would have installed.
There are five images. namely/protoc is protoc with the include directory automatically available. namely/prototool wraps Uber's Prototool, which is a different tool with its own configuration model. namely/protoc-all is a custom generation script that bundles the common use cases. namely/grpc-cli wraps grpc_cli. And namely/gen-grpc-gateway is a custom go-based server for gRPC Gateway, which is a separate project from the gRPC ecosystem and gets its own section in the README.
The images also ship data. The Google APIs are included under /opt/include/google, and the protobuf library artifacts, the well-known types, are under /opt/include/google/protobuf. The README points out the consequence: if you import the well-known types this way, protoc only needs part of the path, that is, -I /opt/include.
The tag scheme ties the image to a gRPC release
Tag conventions are the part of this project most likely to cause confusion, so they are worth stating precisely.
For protoc, grpc_cli and prototool, the pattern is GRPC_VERSION_CONTAINER_VERSION, or GRPC_VERSION_CONTAINER_VERSION-rc.PRERELEASE_NUMBER for pre-releases. The example given is namely/protoc-all:1.15_0, which means gRPC version 1.15 in container version 0. The latest tag always points to the most recent version.
The practical consequence is that there is no floating tag that follows gRPC for you. If you use latest, your generated code can change when the project publishes a new image, and that is exactly the drift the project exists to remove. Pinning a specific tag is the point of the exercise, and the two-part scheme lets you distinguish a new gRPC release from a rebuild of the same release with different plugins.
The release history shows the container versions trailing the gRPC ones. The listed releases are v1.51_2 from 2023-07-25, v1.51_1 from 2022-12-19 and v1.51_0 from 2022-11-29, and the naming follows the same convention with the dot turned into an underscore. The last push to the repository was on 2026-06-09, so the tree is being worked on, but the newest tagged release in the list is from 2023.
That gap is the most important fact for anyone evaluating this now, and it should be checked against the current gRPC release before you commit to the images. The repository is not archived, and the licence is BSD-3-Clause with the terms in the LICENSE.txt file.
A first run is a pull, a cd and one docker run
The usage section starts by pulling the image:
$ docker pull namely/protoc-allThen you change to the directory holding your .proto files, and run the generator with a volume mapping the current directory to /defs, a file flag and a language flag:
$ cd ~/my_project/protobufs
$ docker run -v $PWD:/defs namely/protoc-all -f myproto.proto -l ruby #or go, csharp, etcThe README notes the convention for the command prefixes: commands for bash are prefixed with a dollar sign and commands for PowerShell on Windows are prefixed with PS>, and it says it is not required to use the Windows Subsystem for Linux except for development work on docker-protoc itself. The PowerShell equivalent differs only in how the current directory is passed to the volume mapping.
The output layout is fixed. The container puts the compiled files into a gen directory with language-specific subdirectories, so for Go the files go into ./gen/pb-go and for Ruby into ./gen/pb-ruby.
Three flags adjust that behaviour. The -o flag sets an output directory, which is created automatically, and the README notes that in this case the pb-* subdirectories are not created. The -d flag generates all proto files in a directory and cannot be combined with -f. And the -i flag adds extra include directories, which is the mechanism for lifting proto files up a directory when generating.
That last one has a concrete example. A file at protorepo/catalog/catalog.proto will by default output to gen/pb-go/protorepo/catalog/ because protorepo is part of the input path. To remove it you add the include and change the import, so the -i protorepo form with -f catalog/catalog.proto avoids the extra directory that the other form produces.
Language-specific flags exist where the plugin needs them
Beyond the common flags, the README documents a small set of options for particular targets, which is the point of the protoc-all image over the plain one.
For Ruby there is a --with-rbi flag that generates Ruby Sorbet type definition .rbi files. For node and web code generation there are three flags that modify the underlying protoc options: --js-out changes the js_out option, --grpc-web-out changes the grpc-web_out option, and --grpc-out changes the grpc_out option, with the README pointing at the npm grpc-tools package for more detail on that last one.
Those flags exist because each language plugin takes its own output option in protoc, and the script inside the image translates a single --language flag into the right set of per-language options. That translation is the substance of the protoc-all image, and it is why a plain protoc image will not produce the same file layout even when it can produce the same code.
The support matrix in the features list gives the coverage: all C-based gRPC libraries with Go and Java native libraries. The Go and Java side is the one with native gRPC support, and the other languages are served through the community plugins the image installs.
The README also carries a troubleshooting section for people having trouble, and the note about the WSL requirement is placed early for a reason: the volume mapping syntax differs between shells, and getting that wrong is the most common first-run failure.
The gateway image produces a runnable server, not just files
The gRPC Gateway part is a different kind of image. namely/gen-grpc-gateway generates a server that acts as an HTTP server and a gRPC client to your gRPC service, from a proto annotated with the google.api.http rules.
Generating the project takes one run:
docker run -v `pwd`:/defs namely/gen-grpc-gateway -f path/to/your/proto.proto -s Servicewhere Service is the name of the gRPC service defined in the proto. The output is a folder with a simple go server, by default placed in the gen/grpc-gateway folder, and you then build the contents of that folder into an actual runnable gateway server:
docker build -t my-grpc-gateway gen/grpc-gateway/There is a specific failure the README warns about. If your service contains no google.api.http annotations, this build will fail with an error saying HandlerFromEndpoint is undefined. The fix is either to have at least one rpc method annotated, or to use the --generate-unbound-methods option to expose all the methods in the proto file. That is a helpful detail, because the error message points at a missing symbol rather than at the missing annotation that causes it.
Running the built image takes a backend address:
docker run my-grpc-gateway --backend=grpc-service:50051The gateway listens on port 80 for HTTP traffic. Configuration is done with spf13/viper, with the options documented in a config template in the repository, and a prefix is set through the proxy.api-prefix setting. The example given is a google.api.http annotation of /foo/bar with an api-prefix of /api/, so the gateway answers on /api/foo/bar. The same thing can be set with an environment variable named after the service, so the prefix can be changed without rebuilding the image.
The build clones gRPC from source with Bazel
The Dockerfile is the part that explains why these images are large and why the build takes a while.
It starts from a golang image parameterized by Debian version and Go version, and it declares build arguments for every component it needs to assemble: the gRPC version, the grpc-gateway version, the grpc-java version, the Scala protobuf version, the protobuf-javascript version, the Go plugin versions, the mypy version, the uber prototool version and the node grpc-tools versions. Each of those arguments is declared without a value in the Dockerfile itself, so the values are supplied from outside it, and the repository carries a variables.sh file at the root that is the natural place to change them.
The build then clones the upstream sources. The gRPC repository is cloned at the tag for the requested version with shallow clones and shallow submodules, the same for grpc-java, and protobuf-javascript is cloned for its version. The Google APIs and the api-common-protos repositories are cloned too, which is what produces the /opt/include/google content the README describes.
The gRPC compiler is then built with Bazel, using the Bazel binary that ships inside the gRPC tree at tools/bazel, with the build cache mounted from the Docker build cache so repeated builds are faster.
The Makefile at the root is a thin wrapper over the shell scripts, with build, test, test-gwy, push, push-latest and tag-latest targets. Two of the comments are telling: the push target says it is not for manual invocation, and the tag-latest target points at the release workflow in .github/workflows/release.yml. So the publishing path is automated and the Makefile is for local work.
There is also a cli/ directory and an all/ directory alongside the gwy/ directory for the gateway, which correspond to the several images the project publishes rather than to a single application.
Editorial conclusion
docker-protoc is worth adopting if your team has more than one person generating protobuf bindings, because the whole class of problems where a developer's protoc version does not match the one in CI disappears when the generator is a container, and the bundled Google API protos remove a second source of drift. It is the wrong tool if you generate occasionally and your existing protoc works, since a container adds a layer between you and the error message, and it is the wrong tool if you need the newest gRPC features, because the image tags are pinned to specific gRPC versions. Before you adopt it, verify three things yourself: that the tag you pin matches the gRPC release your other services use, since the convention is GRPC_VERSION_CONTAINER_VERSION, that the include path is right for the well-known types, since the image exposes them under a different root and a wrong include gives an import error that looks like a missing file, and that the language-specific plugins are present in the image you pick, because the plain protoc image only carries the compiler and the all image is the one with the full plugin set.
Frequently asked questions
What does the namely/docker-protoc project provide?
It provides Docker images wrapping protoc, Uber's Prototool and grpc_cli with gRPC support for several languages, plus a custom generation script image and a gRPC Gateway server generator, so you can generate protobuf bindings without installing and managing those tools locally.
How do I generate protobuf code with docker-protoc?
Pull namely/protoc-all, change to the directory containing your .proto files, and run the container with your current directory mapped to /defs, for example docker run -v $PWD:/defs namely/protoc-all -f myproto.proto -l ruby. The output goes into a gen directory with language-specific subdirectories such as ./gen/pb-go and ./gen/pb-ruby.
What is the tag convention for the docker-protoc images?
The pattern is GRPC_VERSION_CONTAINER_VERSION, for example namely/protoc-all:1.15_0, or GRPC_VERSION_CONTAINER_VERSION-rc.PRERELEASE_NUMBER for pre-releases. The latest tag always points to the most recent version, so pinning an explicit tag is what keeps generated code consistent.
How do I generate a gRPC Gateway server with docker-protoc?
Run namely/gen-grpc-gateway with the proto file and the service name, for example docker run -v `pwd`:/defs namely/gen-grpc-gateway -f path/to/your/proto.proto -s Service. That creates a go server in the gen/grpc-gateway folder which you can build with docker build, and the gateway listens on port 80.
Why does my gRPC Gateway build fail with HandlerFromEndpoint is undefined?
The README explains that the build fails that way if your service contains no google.api.http annotations. You need at least one annotated rpc method, or you can use the --generate-unbound-methods option to expose all the methods in the proto file.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/namely-docker-protoc)Was this useful?