Developing a Kong Plugin with Go

When trying to develop a custom Kong plugin with Go, I couldn't find any working examples for the latest Kong version (3.9.0 at the time of writing). This post demonstrates a simple, working Go plugin example to build upon. For reference and further reading, visit the official documentation's Develop Custom Plugins section.
Setup Basic Kong Configuration
Create the kong.yml
and docker-compose.yml
files. This example uses https://reqres.in/api/users/1
as a mock service. The goal is to forward requests sent to test-service
's test-route
to the mock service URL and return the response.
kong.yml
_format_version: "3.0"
services:
- name: test-service
url: https://reqres.in/api/users/1
routes:
- name: test-route
paths:
- /
strip_path: false
docker-compose.yml
name: kong-go-plugin
services:
kong:
container_name: kong
image: kong:latest
volumes:
- ./kong.yml:/kong/declarative/kong.yml
environment:
- KONG_DATABASE=off
- KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml
- KONG_PROXY_ACCESS_LOG=/dev/stdout
- KONG_ADMIN_ACCESS_LOG=/dev/stdout
- KONG_PROXY_ERROR_LOG=/dev/stderr
- KONG_ADMIN_ERROR_LOG=/dev/stderr
- KONG_ADMIN_LISTEN=0.0.0.0:8001
- KONG_ADMIN_GUI_URL=http://localhost:8002
ports:
- "8000:8000"
- "8443:8443"
- "8001:8001"
- "8444:8444"
- "8002:8002"
- "8445:8445"
- "8003:8003"
- "8004:8004"
Deploy Basic Kong Configuration
Start Kong with this command:
docker compose up -d
Test the setup by sending a request to Kong:
curl http://localhost:8000
The mock service should respond with:
{"data":{"id":1,"email":"george.bluth@reqres.in","first_name":"George","last_name":"Bluth","avatar":"https://reqres.in/img/faces/1-image.jpg"},"support":{"url":"https://contentcaddy.io?utm_source=reqres&utm_medium=json&utm_campaign=referral","text":"Tired of writing endless social media content? Let Content Caddy generate it for you."}}
Create the Go Plugin
Create a file named plugin.go
that adds a custom header to the response:
go mod init test-plugin
go get github.com/Kong/go-pdk
go get github.com/Kong/go-pdk/server
package main
import (
"github.com/Kong/go-pdk"
"github.com/Kong/go-pdk/server"
)
const (
version = "0.0.1"
priority = 1
)
type Config struct {
Message string `json:"message"`
}
func New() interface{} {
return &Config{}
}
func (c *Config) Access(kong *pdk.PDK) {
kong.Response.AddHeader("X-Test-Header", c.Message)
}
func main() {
_ = server.StartServer(New, version, priority)
}
This plugin accepts a message
configuration parameter and adds it as a value in the X-My-Header
response header. The Access
function is called during Kong's request processing lifecycle. The plugin version ("0.0.1") and priority (1) are defined as constants - Kong executes plugins in order from highest to lowest priority. For more advanced features, check the official documentation's Write Plugins in Go section.
Compile the Plugin
Build the plugin, cross-compiling is necessary unless you are developing on Linux, since Kong runs in a Linux container.
go mod tidy
GOOS=linux go build -o test-plugin plugin.go
Update Kong Configuration
Update kong.yml
to add the plugin to the test service:
_format_version: "3.0"
services:
- name: test-service
url: https://reqres.in/api/users/1
routes:
- name: test-route
paths:
- /
strip_path: false
plugins:
- name: test-plugin
config:
message: "Test message!"
plugins:
- name: test-plugin
enabled: true
Modify docker-compose.yml
to copy the compiled plugin into the Kong container and set environment variables to enable and configure the plugin:
name: kong-go-plugin
services:
kong:
container_name: kong
image: kong:latest
volumes:
- ./kong.yml:/kong/declarative/kong.yml
- ./test-plugin:/usr/local/bin/test-plugin
environment:
- KONG_DATABASE=off
- KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml
- KONG_PROXY_ACCESS_LOG=/dev/stdout
- KONG_ADMIN_ACCESS_LOG=/dev/stdout
- KONG_PROXY_ERROR_LOG=/dev/stderr
- KONG_ADMIN_ERROR_LOG=/dev/stderr
- KONG_ADMIN_LISTEN=0.0.0.0:8001
- KONG_ADMIN_GUI_URL=http://localhost:8002
- KONG_PLUGINS=test-plugin
- KONG_PLUGINSERVER_NAMES=test-plugin
- KONG_PLUGINSERVER_TEST_PLUGIN_SOCKET=/usr/local/kong/test-plugin.socket
- KONG_PLUGINSERVER_TEST_PLUGIN_START_CMD=/usr/local/bin/test-plugin
- KONG_PLUGINSERVER_TEST_PLUGIN_QUERY_CMD=/usr/local/bin/test-plugin -dump
ports:
- "8000:8000"
- "8443:8443"
- "8001:8001"
- "8444:8444"
- "8002:8002"
- "8445:8445"
- "8003:8003"
- "8004:8004"
Deploy with the Plugin
Restart the container with the updated configuration:
docker compose down
docker compose up -d
Test the plugin by sending a request with verbose output to see the custom header:
curl -v http://localhost:8000
The response should contain the custom header along with the data:
...
< X-Test-Header: Test message!
...
{"data":{"id":1,"email":"george.bluth@reqres.in","first_name":"George","last_name":"Bluth","avatar":"https://reqres.in/img/faces/1-image.jpg"},"support":{"url":"https://contentcaddy.io?utm_source=reqres&utm_medium=json&utm_campaign=referral","text":"Tired of writing endless social media content? Let Content Caddy generate it for you."}}
The final project layout:
.
├── docker-compose.yml
├── go.mod
├── go.sum
├── kong.yml
├── plugin.go
└── test-plugin
This guide demonstrates developing a custom Kong plugin with Go. This simple example can serve as a starting point for more complex plugin development.
Subscribe to my newsletter
Read articles from Erman İmer directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
