Introduction
The next-generation plugin compiler (NG) is a second build of the Tyk Plugin Compiler image. It is published from the same repositories with a-ng suffix on the tag, and takes the same arguments.
NG is opt-in. Both compilers are maintained in parallel, and nothing about the existing compiler changes.
If you are new to building Go plugins, start with Golang Plugins. This page describes only what NG adds and how it differs.
NG images are published for released Gateway versions, starting with 5.15.0 and the 5.13.2 LTS. The existing plugin compiler is still built and published for the same versions, so you can stay on it.
Why use it
A Go plugin must be built with the same toolchain, build tags and build flags as the Gateway that loads it. NG keeps that contract exactly, and changes what surrounds it.A much smaller vulnerability surface
NG is built on a Docker Hardened Image. Its C toolchain is provisioned by Docker rather than installed at build time, and Docker publishes assessments for the packages it ships. Scanned with Tyk’s published VEX feed applied, the NG image reports 0 Critical and 0 High findings. This applies to the FIPS image too, so a FIPS build no longer means accepting a larger set of findings. See Scanning the image to reproduce it yourself.Native ARM64 support
The image is published forlinux/arm64 as well as linux/amd64. On Apple silicon or AWS Graviton it runs on the host architecture, not through emulation. Builds are therefore quicker, and they behave the same as they do on an x86 machine.
More target architectures from one image
A single NG image cross-compiles, so you no longer need a separate build path per architecture. The Community image builds forlinux/amd64, linux/arm64 and linux/s390x. The EE and FIPS images build for linux/amd64 and linux/arm64, because no s390x Gateway is published for those editions.
Problems surface at build time, not at load time
After a build, NG inspects the plugin it produced and checks the architecture, Go version, edition, FIPS mode and glibc requirements. If any of them do not match what the Gateway expects, the build fails with a message explaining why. The plugin therefore does not fail later, when the Gateway tries to load it.Compatibility with older Linux distributions is preserved
Plugins are linked against glibc 2.17. They keep running on the same range of systems as before, even though the image itself is built on a modern base.Choosing between the two compilers
Use the existing compiler if you are building plugins for a released Gateway version and your build works today.
Try NG in these cases:
- You want to build for more than one architecture from one image.
- You want your builds to run natively on an ARM64 machine.
- You want the build to fail early rather than at load time.
- You need the vulnerability posture of the hardened base.
Image variants
NG is published from the same three Docker Hub repositories as the existing compiler:
The edition is baked into each image, together with the build tags,
GOFIPS140 / GOEXPERIMENT settings and the list of architectures that edition’s Gateway is published for. You do not need to set EDITION yourself. You cannot use the CE image to produce an EE or FIPS plugin, and the build fails with an explicit message if you try.
Match the image to the Gateway you will load the plugin into. An ee plugin will not load into a CE Gateway, and a FIPS Gateway will reject a plugin built without FIPS crypto.
The hardened base used by NG is a supply-chain hardening measure. It is not in itself a FIPS compliance claim. FIPS plugin crypto comes from the FIPS Gateway’s Go toolchain and the edition settings baked into the FIPS image.
Quick start
Mount your plugin source at/plugin-source and pass the output name. The entrypoint is /build.sh, the working directory is /go/src/github.com/TykTechnologies/tyk, and the container runs as root — exactly as with the existing compiler:
/plugin-source, so it appears in your working directory. The output file name is {plugin_name%.*}_{GATEWAY_VERSION}_{GOOS}_{GOARCH}.so — for the example above, plugin_v5.15.0_linux_amd64.so.
For Enterprise or FIPS plugins, change the repository:
Positional arguments
The four positional arguments are unchanged from the existing compiler:plugin_name— the output name, for examplevendor-plugin.so. It must be a bare file name, not a path.plugin_id— optional. It selects the build directory inside the container, and supplies the generated module name when your plugin has nogo.mod.GOOS— optional override.GOARCH— optional override.
NG never rewrites an existing plugin
go.mod module path or the import paths in your Go sources. The existing compiler rewrites both when plugin_id is supplied.If your workflow relied on that rewrite to build the same plugin twice into one Gateway, set the module path in your own go.mod instead.Environment variables
The glibc target is a property of the image rather than a runtime setting. Each image ships exactly one sysroot set, and published NG images ship glibc 2.17.
Cross-compiling
Pass the target as the third and fourth positional arguments:plugin_v5.15.0_linux_arm64.so. s390x works the same way with the CE image:
s390x fails. The message lists the architectures that edition supports. No such Gateway exists to load the plugin.
The compiler images run natively on both linux/amd64 and linux/arm64, so on Apple silicon or Graviton the build is not emulated. The target architecture is still a separate choice from the host: set GOARCH explicitly whenever you want something other than the machine you are on.
Post-build validation
After the build, NG inspects the produced.so. This happens before the artifact is moved back to /plugin-source. NG checks that:
- The file is a 64-bit ELF shared object built with
-buildmode=plugin. - The ELF machine type matches the requested
GOARCH. - The Go toolchain recorded in the plugin matches the Gateway’s Go version.
- For
eeandee-fips, theeebuild tag is present. - For
ee-fips, the binary carries FIPS crypto evidence. - The linked libraries are ones the Gateway provides. A plugin linked against musl or
libpythonis rejected, because the Gateway image ships neither. - The highest required
GLIBC_x.ysymbol version is not newer than the image’s glibc target.
github.com/TykTechnologies/tyk revision the plugin links does not match the Gateway revision.
Successful checks are printed as [ok] lines — for example, a line reporting the architecture and a line reporting GLIBC ceiling: 2.17 <= 2.17 — and the run ends with == validation OK: <file> ==.
Interpreting a failure
Failures are printed asERROR (plugin validation): ... and stop the build with a non-zero exit code. The common ones are:
Each of these would otherwise surface as an opaque
plugin was built with a different version of package ... error when the Gateway loads the plugin. If you need the artifact regardless — to inspect it, for example — rerun with -e VALIDATE=0.
Scanning the image for vulnerabilities
NG is built on a Docker Hardened Images base, and Docker publishes affectedness decisions (VEX statements) for the packages it provisions. Those decisions are what make the difference between a raw scan and the reported result. Scanning without VEX reports findings in kernel headers (linux-libc-dev), perl, busybox and libcurl4t64 — packages Docker has assessed as not affecting the image. With Tyk’s VEX repository configured, the same scan reports 0 Critical and 0 High.
Configuring the VEX repository
Create the Trivy VEX repository configuration at$XDG_DATA_HOME/.trivy/vex/repository.yaml, or at $HOME/.trivy/vex/repository.yaml when XDG_DATA_HOME is not set:
--vex repo:
Three things are easy to get wrong here:
- The
urlis the site root. Sub-paths such as/tykor/dhido not exist and return 404. - If you have other VEX repositories configured, list
tyk-dhifirst. A repository that merely lists a package takes precedence over lower-priority repositories for that package. This applies even when it carries no statement for the CVE in question. - Pass
--show-suppressed. Without it, suppressed findings disappear from the report with no indication that anything was suppressed.
Things to know before you switch
- Each image ships one glibc sysroot set — 2.17 in published images. There is no runtime switch to a different glibc target.
- The edition is fixed per image.
EDITION=eeorEDITION=ee-fipsfails in an image that was not built for that edition. linux/s390xis available for the Community edition only, because EE and FIPS Gateways are not published for that architecture.- Published NG images deliberately contain no Gateway executable, so you cannot run
tyk plugin loadinside the compiler container. Load and exercise your plugin in a Gateway container instead. plugin_idno longer rewrites your module path or import paths. See Positional arguments.
Relationship to the existing plugin compiler
The compilers are maintained together. NG does not replace the existing plugin compiler, and existing users need change nothing. The existing images continue to be built and published for released Gateway versions, from the same repositories and tags as before. NG is opt-in and interface-compatible. Its entrypoint, working directory, positional arguments, environment variables, source mount point and output naming are the same. Moving a build to NG is normally a tag change, and moving back is the same change in reverse. NG is available alongside released Gateway versions, from 5.15.0 and the 5.13.2 LTS. No date has been set for it to become the default. Use NG where its native ARM64 support, cross-compilation, validation or hardened base are useful. The existing compiler remains fully supported.Migrating from the existing compiler
This section is for anyone who already builds Go plugins withtykio/tyk-plugin-compiler (or the -ee / -fips repositories) and wants to move to NG. You do not have to migrate.
What changes
The image tag. NG is published from the same three repositories with a-ng suffix:
NG is published for released Gateway versions, starting with
v5.15.0-ng and v5.13.2-ng (2026 LTS). There is no NG compiler for Gateway v5.14.
What does not change
- Entrypoint
/build.shand working directory/go/src/github.com/TykTechnologies/tyk. - The container runs as root.
- Plugin source is mounted at
/plugin-source, and the built.sois moved back there. - Positional arguments:
plugin_name, optionalplugin_id, optionalGOOS, optionalGOARCH. - Environment variables you already use:
GOOS,GOARCH,BUILD_TAG,GO_GET,GO_TIDY,DEBUG,PLUGIN_SOURCE_PATH,PLUGIN_BUILD_PATH. - Output naming:
{plugin_name%.*}_{GATEWAY_VERSION}_{GOOS}_{GOARCH}.so.
One behavioural difference to check first
NG never rewrites an existing plugingo.mod module path, and never rewrites import paths in your Go sources. The existing compiler does both when plugin_id is supplied. In NG, plugin_id only names the build directory, and supplies the generated module name when your plugin has no go.mod at all.
If you pass a plugin_id only to isolate the build directory, nothing changes for you. The same applies if you pass it to generate a module name when your plugin has no go.mod. If you relied on the module-path rewrite, set distinct module paths in your own go.mod files instead. That rewrite let you load two builds of the same plugin into one Gateway.
What to check after the first NG build
-
Validation output. NG validates the artifact before it is moved back to
/plugin-source. Confirm the run ends with== validation OK: <file> ==and read the[ok]lines. A failure is a real incompatibility rather than a compiler bug — the same problem would otherwise appear asplugin was built with a different version of package ...when the Gateway loads the plugin.VALIDATE=0skips validation if you need the artifact anyway. - glibc floor. The validation output reports the highest required GLIBC symbol version. It also reports the ceiling it is checked against, which is 2.17 in published NG images. If your plugin fails this check, a cgo dependency is using a symbol newer than the pinned floor. That plugin would have needed a newer runtime glibc than the pinned target.
-
FIPS mode. With the FIPS image, confirm the validator reports a
FIPS:line.EDITION=ee-fips but the plugin shows NO FIPS cryptomeans the artifact would be rejected by a FIPS Gateway. -
Edition tags. With the EE or FIPS image, confirm the
edition: 'ee' build tag presentline. -
Architecture. If you cross-compile, confirm the reported ELF machine type matches your target. The compiler image runs natively on both
linux/amd64andlinux/arm64. SetGOARCHexplicitly whenever your target differs from the machine you build on. -
Load the plugin. Published NG images do not contain a Gateway binary. Run the
tyk plugin loadcheck against a Gateway container, not inside the compiler container.
Rolling back
Drop the-ng suffix and rerun. Nothing else in your build needs to change, and the existing compiler continues to be published for released Gateway versions:
Rebuild the plugin after rolling back rather than reusing an NG artifact, so that the plugin and the toolchain that produced it stay consistent.