Deployment guide

KrakenD API Gateway 2.13.11

Overview

KrakenD is a stateless, declarative, high-performance API gateway. You describe your public API in one JSON file. Each endpoint lists the backend calls behind it, and KrakenD routes, aggregates, filters and secures those calls. JWT validation, rate limiting, circuit breakers and response manipulation are built in.

KrakenD keeps no database, so a gateway is just a binary and a configuration file.

This guide covers launching and using the KrakenD API Gateway 2.13.11 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud application infrastructure listing on AWS Marketplace. You choose it as the software version when you subscribe and launch.

What you get is a KrakenD that is safe to put on a public address from the first minute:

  • HTTPS from the first boot. nginx terminates TLS on port 443 with a certificate generated on your instance's first boot (TLS 1.2 or newer) and hands requests to KrakenD on 127.0.0.1:8080. No certificate or key is baked into the image, and nothing listens on port 80.
  • Never an open proxy. KrakenD serves only the endpoints declared in /etc/krakend/krakend.json. The shipped endpoints call one fixed demo backend on 127.0.0.1:8081, which you replace with your own services. Requests for other host names, and absolute URLs aimed at another host (such as the instance metadata address), are dropped.
  • Internal endpoints stay private. KrakenD's /__health answers on loopback only, its /__debug and /__echo endpoints are switched off, no metrics listener is configured, and nginx returns 404 for every /__ path. The public /health probe returns ok only while KrakenD is healthy.
  • A JWT-protected endpoint with a per-instance key. /v1/secure/users/{id} is protected by KrakenD's JWT validator against an HS256 signing key generated on your instance's first boot. A demo token is in a root-only file, and a helper mints more.
  • The real client address. nginx overwrites X-Forwarded-For and X-Real-IP with the connection's address and drops client-supplied Forwarded and X-Forwarded-Host headers. KrakenD trusts forwarding headers from 127.0.0.1 only.
  • Sandboxed and fail-closed. KrakenD runs as the unprivileged krakend user with no capabilities, under a locked-down systemd unit that cannot reach the instance metadata service. KrakenD's anonymous usage reporting is switched off. KrakenD and nginx do not start until first boot has generated the keys, and the configuration is checked against KrakenD's schema before every start.
  • Built from source, with the advisories fixed. The upstream 2.13.11 release binary carries 15 Go security advisories that govulncheck reports as reachable. This image compiles the pinned 2.13.11 source with Go 1.26.10 and patched gRPC, OpenTelemetry, AMQP and x/net modules. govulncheck reports 0 reachable advisories, and the binary is reproducible (Step 11).
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: KrakenD 2.13.11 Community Edition (/usr/local/bin/krakend, with the licence and build provenance in /usr/share/doc/krakend), the gateway configuration /etc/krakend/krakend.json, nginx, the krakend.service and krakend-firstboot.service units, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu | Architecture: x86_64 | Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route that you use for management).
  • A security group that allows TCP 22 (SSH) from your own networks only and TCP 443 (HTTPS) from the clients that call your API. Nothing else needs to be open. KrakenD and the demo backend listen on loopback only, and there is no port 80 listener.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick KrakenD API Gateway 2.13.11 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2.

Choose m5.large, your key pair and a security group as described above. KrakenD is light, so size up only for high request rates.

Alternatively, launch with the AWS CLI. Replace the values in angle brackets:

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=krakend}]'
2

Connect to your instance

Version SSH user

KrakenD API Gateway 2.13.11 on Ubuntu 24.04 ubuntu

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot generates the certificate, the JWT signing key and a demo token, then starts KrakenD and nginx. Check that the services are running:

systemctl is-active krakend nginx
sudo test -f /var/lib/sac/data/krakend-firstboot.done && echo FIRSTBOOT_DONE krakend version
active active FIRSTBOOT_DONE KrakenD Version: 2.13.11 Go Version: 1.26.10 Glibc Version: STATIC-CGO-DISABLED

nginx listens on port 443 on every address. KrakenD (8080) and the demo backend (8081) listen on loopback only:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | sed -E 's/pid=[0-9]+,fd=[0-9]+/.../g' | sort -u
0.0.0.0:22 users:(("sshd",...),("systemd",...)) 0.0.0.0:443 users:(("nginx",...),("nginx",...),("nginx",...)) 127.0.0.1:8080 users:(("krakend",...)) 127.0.0.1:8081 users:(("nginx",...),("nginx",...),("nginx",...)) 127.0.0.53%lo:53 users:(("systemd-resolve",...)) 127.0.0.54:53 users:(("systemd-resolve",...)) [::]:22 users:(("sshd",...),("systemd",...)) [::]:443 users:(("nginx",...),("nginx",...),("nginx",...))
4

Your instance's URL and demo token

sudo cat /root/krakend-credentials.txt

The file is readable by root only. It holds the instance URL and the demo token:

# KrakenD API Gateway 2.13.11 - generated on first boot by krakend-firstboot.service
# The JWT signing key and the demo token are unique to this instance; none was baked into the image. Keep this secret. KRAKEND_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/ KRAKEND_CERT_SHA256=<sha256-fingerprint> KRAKEND_CONFIG=/etc/krakend/krakend.json KRAKEND_JWK_FILE=/etc/krakend/jwk-sac.json KRAKEND_JWT_ISSUER=sac-krakend-demo KRAKEND_DEMO_TOKEN=<KRAKEND_DEMO_TOKEN>
#
# nginx (HTTPS) is the only public entry point; KrakenD runs on 127.0.0.1:8080 and the demo backend on 127.0.0.1:8081.
# KRAKEND_DEMO_TOKEN (30 days) opens /v1/secure/users/{id}:
curl -H "Authorization: Bearer <token>" .../v1/secure/users/1
# New demo token:
sudo krakend-demo-token --ttl 3600
# New signing key + token:
sudo krakend-rotate-jwt-key
# Change the name it serves as:
sudo krakend-set-hostname <your-dns-name>
# User guide: https://secureanycloud.com/stacks/krakend-api-gateway-2-13-11/

The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve your own DNS name, see Step 9.

5

Send requests through KrakenD

The shipped configuration declares three endpoints in front of the demo backend. /v1/hello proxies one backend call. /v1/users/{id} calls the backend twice (the user record and the user's orders) and merges both answers into one response.

First call the gateway from the instance itself, trusting the instance's own certificate:

H=$(sudo sed -n 's#^KRAKEND_URL=https://\(.*\)/$#\1#p' /root/krakend-credentials.txt)
curl -s --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H/v1/hello" | jq .
curl -s --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H/v1/users/1" | jq .
curl -s -o /dev/null -D - --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H/v1/hello" | tr -d '\r' | grep -iE '^(HTTP|content-type|x-krakend|strict-transport|x-content)'
{ "message": "Hello from the demo backend on 127.0.0.1:8081, routed through KrakenD", "service": "sac-krakend-demo-backend" } { "orders": { "orders": [ { "id": 1001, "status": "shipped", "total": 42.50 }, { "id": 1002, "status": "processing", "total": 19.99 } ] }, "user": { "email": "ada@example.com", "id": 1, "name": "Ada Lovelace", "plan": "enterprise" } } HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 X-Krakend: Version 2.13.11 X-Krakend-Completed: true Strict-Transport-Security: max-age=31536000 X-Content-Type-Options: nosniff

Then call the gateway over the internet from your own machine. The certificate is self-signed, so -k skips the check. Step 9 covers your own name and certificate:

curl -k https://<public-ip>/v1/users/2

KrakenD answers only the endpoints you declare. Any other path returns 404. A host name the instance does not serve is dropped. An absolute URL that tries to use the gateway as a forward proxy to another host (here the instance metadata address) is dropped. Nothing answers on port 80:

H=$(sudo sed -n 's#^KRAKEND_URL=https://\(.*\)/$#\1#p' /root/krakend-credentials.txt) for p in /v1/nope /latest/meta-data/; do curl -s -o /dev/null -w "$p -> %{http_code}\n" --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H$p"; done curl -sk -o /dev/null -w 'unknown host name -> %{http_code}\n' --resolve "other.example:443:127.0.0.1" "https://other.example/v1/hello" curl -sk -o /dev/null -w 'absolute URL for another host -> %{http_code}\n' --request-target 'http://169.254.169.254/latest/meta-data/' -H "Host: $H" https://127.0.0.1/ curl -s -o /dev/null -w 'port 80 -> %{http_code}\n' http://127.0.0.1/ || true
/v1/nope -> 404 /latest/meta-data/ -> 404 unknown host name -> 000 absolute URL for another host -> 000 port 80 -> 000

000 means the connection was closed without an answer.

6

The JWT-protected endpoint

/v1/secure/users/{id} is protected by KrakenD's auth/validator. It accepts only an HS256 token signed with this instance's key (/etc/krakend/jwk-sac.json, generated on first boot), issued by sac-krakend-demo, with the role demo. Without a valid token, the request never reaches the backend:

H=$(sudo sed -n 's#^KRAKEND_URL=https://\(.*\)/$#\1#p' /root/krakend-credentials.txt)
T=$(sudo sed -n 's/^KRAKEND_DEMO_TOKEN=//p' /root/krakend-credentials.txt)
curl -s -o /dev/null -w 'no token -> %{http_code}\n' --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H/v1/secure/users/3"
curl -s -o /dev/null -w 'bad token -> %{http_code}\n' -H 'Authorization: Bearer not.a.token' --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H/v1/secure/users/3"
curl -s -K <(printf 'header = "Authorization: Bearer %s"\n' "$T") --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H/v1/secure/users/3" | jq .
no token -> 401 bad token -> 401 { "email": "grace@example.com", "id": 3, "name": "Grace Hopper", "plan": "starter" }

curl -K reads the header from a file descriptor, so the token never appears on a command line.

Mint more tokens with krakend-demo-token (--sub, --ttl in seconds, --role). A token with another role gets 403:

T2=$(sudo krakend-demo-token --sub alice --ttl 600)
echo "$T2" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | jq -c '{iss, sub, roles}'
H=$(sudo sed -n 's#^KRAKEND_URL=https://\(.*\)/$#\1#p' /root/krakend-credentials.txt)
curl -s -o /dev/null -w 'new token -> %{http_code}\n' -K <(printf 'header = "Authorization: Bearer %s"\n' "$T2") --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H/v1/secure/users/2"
{"iss":"sac-krakend-demo","sub":"alice","roles":["demo"]} new token -> 200

sudo krakend-rotate-jwt-key replaces the signing key, so every older token stops working. It also writes a new demo token and restarts KrakenD.

In production, you will usually point the validator at your identity provider's JWKS URL (jwk_url) instead of a local key. The KrakenD JWT validation documentation covers every option.

7

Health and internal endpoints

Load balancers and monitoring can use the public /health probe. nginx answers ok only while KrakenD's own /__health reports healthy.

KrakenD's internal endpoints are never reachable from the network, and the debug and echo endpoints are switched off even on loopback:

H=$(sudo sed -n 's#^KRAKEND_URL=https://\(.*\)/$#\1#p' /root/krakend-credentials.txt) for p in /health /__health /__debug/ /__echo/ /__stats; do curl -s -o /dev/null -w "public $p -> %{http_code}\n" --cacert /etc/nginx/tls/krakend.crt --resolve "$H:443:127.0.0.1" "https://$H$p"; done curl -s http://127.0.0.1:8080/__health | jq -c '{status}' curl -s -o /dev/null -w 'loopback /__debug/ -> %{http_code}\n' http://127.0.0.1:8080/__debug/
public /health -> 200 public /__health -> 404 public /__debug/ -> 404 public /__echo/ -> 404 public /__stats -> 404 {"status":"ok"} loopback /__debug/ -> 404

If you enable KrakenD's metrics or OpenTelemetry exporters later, bind them to 127.0.0.1 and never open their ports in a security group.

8

Put your own services behind KrakenD

Edit /etc/krakend/krakend.json and replace the demo endpoints with your own API. Each endpoint lists its backends. A backend can be a service on this instance or a private address in your VPC:

sudo nano /etc/krakend/krakend.json

For example, an endpoint that proxies an orders service on a private host:

{ "endpoint": "/v1/orders/{id}", "method": "GET", "backend": [ { "host": ["http://10.0.1.25:3000"], "url_pattern": "/orders/{id}" } ] }

Keep "listen_ip": "127.0.0.1", because nginx is the public entry point. Leave debug_endpoint and echo_endpoint off.

Check the file against KrakenD's built-in schema before you restart, so that a typing mistake never takes the gateway down. The unit runs the same check before every start:

sudo krakend check -c /etc/krakend/krakend.json -n
Parsing configuration file: /etc/krakend/krakend.json Syntax OK!
sudo systemctl restart krakend

The demo backend (127.0.0.1:8081) can stay or go. Once your own services are behind KrakenD, you can remove its server block from /etc/nginx/sites-available/sac-krakend. Whatever you route to, KrakenD cannot reach the instance metadata service (Step 10).

Plugins: the KrakenD on this image is a static build (CGO_ENABLED=0), so KrakenD 2.x Go plugins (.so files) cannot be loaded. Every built-in KrakenD Community Edition component works. KrakenD 3.0 removed plugin support upstream.

9

Your own DNS name and certificate

Point a DNS record at the instance, then set the name that the gateway serves. This creates a new self-signed certificate for the name, updates the nginx host settings, reloads nginx and keeps the name across reboots:

sudo krakend-set-hostname <your-domain>

To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running krakend-set-hostname again replaces it with a self-signed one:

sudo install -m 0644 <your-domain>.crt /etc/nginx/tls/krakend.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/tls/krakend.key
sudo nginx -t && sudo systemctl reload nginx
10

Check the security posture

The certificate was generated on this instance and names its own addresses. The TLS key and the credentials file are readable only by root, and the JWT signing key only by root and the krakend group:

H=$(sudo sed -n 's#^KRAKEND_URL=https://\(.*\)/$#\1#p' /root/krakend-credentials.txt)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
sudo stat -c '%a %U:%G %n' /etc/nginx/tls/krakend.key /etc/nginx/tls/krakend.crt /etc/krakend/jwk-sac.json /etc/krakend/krakend.json /root/krakend-credentials.txt
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost 600 root:root /etc/nginx/tls/krakend.key 644 root:root /etc/nginx/tls/krakend.crt 640 root:krakend /etc/krakend/jwk-sac.json 640 root:krakend /etc/krakend/krakend.json 600 root:root /root/krakend-credentials.txt

The client address comes from the connection. Suppose a request claims to come from somewhere else in X-Forwarded-For and names another host in X-Forwarded-Host. nginx and KrakenD log it with its real address, and the backend receives only that address and the configured host name:

H=$(sudo sed -n 's#^KRAKEND_URL=https://\(.*\)/$#\1#p' /root/krakend-credentials.txt)
P=$(hostname -I | awk '{print $1}')
curl -sk -o /dev/null --resolve "$H:443:$P" -H 'X-Forwarded-For: 203.0.113.9' -H 'X-Forwarded-Host: evil.example' "https://$H/v1/users/spoof-test" sleep 1
sudo grep -h 'spoof-test' /var/log/nginx/access.log | tail -1 | cut -d' ' -f1,6-9 sudo journalctl -u krakend --no-pager -o cat | grep 'GIN.*spoof-test' | tail -1 | tr -s ' '
sudo grep -h 'users/spoof-test' /var/log/nginx/krakend-demo-backend.log | tail -1
<private-ip> "GET /v1/users/spoof-test HTTP/1.1" 500 [GIN] <time> | 500 | <duration> | <private-ip> | GET "/v1/users/spoof-test" 127.0.0.1 [<time>] "GET /users/spoof-test HTTP/1.1" 404 xff="<private-ip>" xfh="ec2-<public-ip-dashed>.compute-1.amazonaws.com" host="127.0.0.1" ua="KrakenD Version 2.13.11"

The 500 is expected: the demo backend has no user called spoof-test.

KrakenD runs as the krakend user with no capabilities and cannot reach the instance metadata service. It does not report usage to KrakenD's servers. It does not start before first boot has finished, or with a configuration that fails the schema check:

systemctl show krakend -p User -p NoNewPrivileges -p ProtectSystem -p ProtectHome -p CapabilityBoundingSet -p IPAddressDeny
systemctl cat krakend | grep -E '^(ExecStartPre|Environment)='
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 CapabilityBoundingSet= User=krakend ProtectHome=yes ProtectSystem=strict NoNewPrivileges=yes Environment=USAGE_DISABLE=1 ExecStartPre=+/usr/local/sbin/krakend-guard ExecStartPre=/usr/local/bin/krakend check -c /etc/krakend/krakend.json -n

The metadata block covers every connection KrakenD makes, including calls to backends you configure. An endpoint whose backend is 169.254.169.254 cannot reach the metadata service from inside the krakend service. nginx and the first-boot unit carry the same block.

11

Build provenance

SecureAnyCloud compiled the KrakenD on this image from the unmodified upstream 2.13.11 source. A minimal set of Go modules was raised to clear the advisories in the upstream release binary: gRPC 1.83.2, OpenTelemetry 1.45.0, the RabbitMQ AMQP client 1.14.0 and golang.org/x/net 0.60.0 (plus the modules those require).

The build record, the resulting go.mod and go.sum, and the govulncheck result ship with the image:

ls -l /usr/share/doc/krakend | awk 'NR>1{print $1,$5,$9}' sha256sum /usr/local/bin/krakend sed -n '/^source /,/^toolchain sha256/p' /usr/share/doc/krakend/BUILD-PROVENANCE.txt grep '^Your code' /usr/share/doc/krakend/govulncheck-binary.txt
-rw-r--r-- 3357 BUILD-PROVENANCE.txt -rw-r--r-- 11357 LICENSE -rw-r--r-- 3888 README.md -rw-r--r-- 15459 go.mod -rw-r--r-- 123725 go.sum -rw-r--r-- 301 govulncheck-binary.txt eff034a0e3f0e20082b19c37728dfd6297c10dfba9b77f8804668d6d9d2ae9e3 /usr/local/bin/krakend source https://github.com/krakend/krakend-ce/archive/refs/tags/v2.13.11.tar.gz source sha256 eb69e9f515922d705d865ee75ee3cc9ea0d5787f6f5062c15b77ccb9bb702215 tag commit 1f83305f3f096b7b75ec9c0cde0ec14b542cb1d6 schema https://raw.githubusercontent.com/krakend/krakend-schema/779f6028840e7a5b84ee3593889869a86b89c64f/v2.13/krakend.json (sha256 c7afe896df2485ca182cffd257d4562f1c0d66e110d1ea3dee1ab0a10c2acc6b; byte-identical to the schema in upstream's 2.13.11 binary) toolchain go1.26.10 linux/amd64, official tarball https://go.dev/dl/go1.26.10.linux-amd64.tar.gz toolchain sha256 ccfe7ae822d2c2f41f4fe7c956199967a4dac22d0ee0d2b2bfdb440aab3b20c5 Your code is affected by 0 vulnerabilities.

The build is reproducible: compiling the same source archive, module versions and flags (recorded in BUILD-PROVENANCE.txt) with Go 1.26.10 gives the same SHA-256. The Go toolchain and module cache were used on a temporary in-memory filesystem and are not on the image.

12

Logs

KrakenD writes its startup messages and one line per request, client address included, to the journal. nginx logs public requests to /var/log/nginx/access.log and the demo backend's requests to /var/log/nginx/krakend-demo-backend.log:

sudo journalctl -u krakend --no-pager -o cat | sed 's/\x1b\[[0-9;]*m//g' | grep -E 'Starting KrakenD|Listening on' | tail -2
[KRAKEND] <time> ▶ INFO Starting KrakenD v2.13.11 [KRAKEND] <time> ▶ INFO [SERVICE: Gin] Listening on port: 8080
13

Server components

Component Path

KrakenD binary /usr/local/bin/krakend

Gateway configuration /etc/krakend/krakend.json

JWT signing key (demo endpoint) /etc/krakend/jwk-sac.json

HTTPS front end /etc/nginx/sites-available/sac-krakend

Certificate and key /etc/nginx/tls/

Demo backend content /usr/share/sac-krakend/demo-backend/

Service units krakend.service, krakend-firstboot.service, nginx.service

Metadata block /etc/systemd/system/krakend.service.d/50-sac-imds-deny.conf (and nginx, first boot)

Helpers krakend-set-hostname, krakend-demo-token, krakend-rotate-jwt-key in /usr/local/sbin

Licence and build provenance /usr/share/doc/krakend/

Instance details /root/krakend-credentials.txt (mode 0600)

14

Updating KrakenD

Ubuntu's security updates install automatically. KrakenD is a single binary that apt does not manage on this image. To move to a newer KrakenD release, replace /usr/local/bin/krakend with a newer official release (or your own build), check your configuration with the new binary, and restart:

sudo /tmp/krakend-<version>/krakend check -c /etc/krakend/krakend.json -n sudo install -m 0755 /tmp/krakend-<version>/krakend /usr/local/bin/krakend && sudo systemctl restart krakend

Read the KrakenD release notes first. KrakenD 3.0 changes the configuration format and removes plugins, so test a major upgrade on a copy of the instance.

SecureAnyCloud publishes updated versions of this image as new KrakenD releases ship.

Support

SecureAnyCloud provides 24/7 technical support for this image by email and live chat. Support covers deployment, HTTPS and certificates, designing endpoints and backends, JWT validation, upgrades and troubleshooting. Contact sales@secureanycloud.com.

KrakenD is a trademark of its respective owner. SecureAnyCloud is not affiliated with or endorsed by KrakenD or the KrakenD project.

Deploy on AWS

Launch KrakenD API Gateway 2.13.11 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS