Skip to main content

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 for linux/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 for linux/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.
The interface is identical, so trying NG is usually just a change of image tag.

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:
The built plugin is moved back into /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:
  1. plugin_name — the output name, for example vendor-plugin.so. It must be a bare file name, not a path.
  2. plugin_id — optional. It selects the build directory inside the container, and supplies the generated module name when your plugin has no go.mod.
  3. GOOS — optional override.
  4. 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:
Or as environment variables:
Both forms produce plugin_v5.15.0_linux_arm64.so. s390x works the same way with the CE image:
The set of allowed target architectures is checked against the architectures the chosen edition’s Gateway is published for. Asking the EE or FIPS image for 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 ee and ee-fips, the ee build 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 libpython is rejected, because the Gateway image ships neither.
  • The highest required GLIBC_x.y symbol version is not newer than the image’s glibc target.
It also warns, without failing, when the 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 as ERROR (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:
Download the repository, then scan with --vex repo:
Three things are easy to get wrong here:
  1. The url is the site root. Sub-paths such as /tyk or /dhi do not exist and return 404.
  2. If you have other VEX repositories configured, list tyk-dhi first. 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.
  3. 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=ee or EDITION=ee-fips fails in an image that was not built for that edition.
  • linux/s390x is 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 load inside the compiler container. Load and exercise your plugin in a Gateway container instead.
  • plugin_id no 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 with tykio/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.sh and working directory /go/src/github.com/TykTechnologies/tyk.
  • The container runs as root.
  • Plugin source is mounted at /plugin-source, and the built .so is moved back there.
  • Positional arguments: plugin_name, optional plugin_id, optional GOOS, optional GOARCH.
  • 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 plugin go.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

  1. 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 as plugin was built with a different version of package ... when the Gateway loads the plugin. VALIDATE=0 skips validation if you need the artifact anyway.
  2. 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.
  3. FIPS mode. With the FIPS image, confirm the validator reports a FIPS: line. EDITION=ee-fips but the plugin shows NO FIPS crypto means the artifact would be rejected by a FIPS Gateway.
  4. Edition tags. With the EE or FIPS image, confirm the edition: 'ee' build tag present line.
  5. Architecture. If you cross-compile, confirm the reported ELF machine type matches your target. The compiler image runs natively on both linux/amd64 and linux/arm64. Set GOARCH explicitly whenever your target differs from the machine you build on.
  6. Load the plugin. Published NG images do not contain a Gateway binary. Run the tyk plugin load check 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.