KrakenD
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, with JWT validation, rate limiting, circuit breakers and response manipulation built in. It keeps no database, so a gateway is just a binary and a configuration file.
This guide covers deploying and using the KrakenD Community Edition 3.0.2 on Ubuntu 24.04 LTS image from SecureAnyCloud on Microsoft Azure. KrakenD Community Edition is the open source KrakenD (Apache-2.0). KrakenD Enterprise is a separate commercial product from KrakenD and is not included.
The SecureAnyCloud image gives you a KrakenD that is safe to put on a public address from the first minute.
What you get:
- CVE-2026-111943 fixed and provable. KrakenD 3.0.2 is built with Go 1.26.10, which refuses certificate chains through the eight compromised eMudhra intermediate CAs. The Ubuntu trust store on the image no longer carries the roots those chains end in.
sudo krakend-cve-2026-111943-checkproves both on your VM (Step 9). - HTTPS from the first boot. nginx terminates TLS on port 443 with a certificate generated on your VM'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. Nothing listens on port 80, so an API client pointed athttp://fails instead of sending its token in clear text. - Never an open proxy. KrakenD only serves the endpoints declared in
/etc/krakend/krakend.json. The shipped endpoints call one fixed demo backend on127.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 Azure metadata address), are dropped. - Internal endpoints stay private. KrakenD's
/__healthanswers on loopback only. Its/__debugand/__echoendpoints are switched off, no metrics listener is configured, and nginx returns 404 for every/__path. The public/healthprobe returnsokonly while KrakenD is healthy. - A JWT-protected endpoint with a per-VM key.
/v1/secure/users/{id}is protected by KrakenD's JWT validator against an HS256 signing key generated on your VM'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-ForandX-Real-IPwith the connection's address and drops client-suppliedForwardedandX-Forwarded-Hostheaders. KrakenD trusts forwarding headers from127.0.0.1only. - Sandboxed and fail-closed. KrakenD runs as the unprivileged
krakenduser with no capabilities, under a locked-down systemd unit that cannot reach the Azure Instance Metadata Service or the WireServer. KrakenD's anonymous usage reporting is switched off. KrakenD and nginx will not start until first boot has generated the keys, and the configuration is checked against KrakenD's schema before every start. - Built from the upstream source. The image compiles the tagged 3.0.2 source with Go 1.26.10 (the Go release KrakenD builds 3.0.2 with). It raises one library,
golang.org/x/net, to v0.62.0 for two advisories published after KrakenD's release.govulncheckreports 0 reachable advisories, and the binary is reproducible (Step 12). - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on, no root login over SSH, and SSH password logins left to the choice you make in the Azure portal.
What is on the image: KrakenD Community Edition 3.0.2 (/usr/local/bin/krakend, license and build provenance in /usr/share/doc/krakend), the gateway configuration /etc/krakend/krakend.json (KrakenD 3.0 configuration format, "version": 4), nginx, the krakend.service, krakend-firstboot.service and krakend-address.service units, Ubuntu 24.04 LTS, cloud-init and the Azure Linux agent.
Default user: azureuser (or the name you choose). Architecture: x64. Recommended size: Standard_B2s.
Prerequisites
- An Azure subscription, an SSH key pair and a virtual network in the target region.
- A network security group that allows TCP 22 (SSH) from your admin network 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.
Deploy the VM
In the Azure portal, open Create a resource. Search for KrakenD Community Edition on Ubuntu 24.04 LTS by SecureAnyCloud and select Create.
On the Basics tab, choose Standard_B2s and SSH public key authentication with azureuser as the user name. KrakenD is light, so size up only for high request rates. On the Networking tab, allow 22 and 443 as described above.
To deploy with the Azure CLI instead, replace the values in angle brackets:
az vm image terms accept --urn sac1647283583153:krakend-ubuntu-24-04:default:latest
az vm create --resource-group <resource-group> --name krakend \
--image sac1647283583153:krakend-ubuntu-24-04:default:latest \
--size Standard_B2s --admin-username azureuser --ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard --nsg-rule SSH
az network nsg rule create --resource-group <resource-group> --nsg-name krakendNSG --name krakend-https \
--priority 1010 --access Allow --protocol Tcp --destination-port-ranges 443 --source-address-prefixes <your-clients-cidr>First boot takes well under a minute after the VM is running. It reads the VM's public address and generates the certificate, the JWT signing key and a demo token. Then KrakenD and nginx start.
Connect to your VM
ssh azureuser@<public-ip>Check the services
systemctl is-active krakend-address krakend nginx
sudo test -f /var/lib/sac/data/krakend-firstboot.done && echo FIRSTBOOT_DONE krakend versionactive active active FIRSTBOOT_DONE KrakenD Version: 3.0.2 Go Version: 1.26.10 Glibc Version: STATIC-CGO-DISABLEDnginx 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 -u0.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",...))nginx runs one worker per vCPU, so the number of nginx entries matches your VM size.
Your VM's URL and demo token
sudo cat /root/krakend-credentials.txtThe file is readable by root only:
# KrakenD Community Edition 3.0.2 - generated on first boot by krakend-firstboot.service
# The JWT signing key and the demo token are unique to this VM; none was baked into the image. Keep this secret.
# KrakenD Community Edition is the open-source KrakenD (Apache-2.0). KrakenD Enterprise is a separate commercial product from KrakenD and is not included.
# sac provides 24/7 support for this image, the Ubuntu OS and deployment. Support for KrakenD itself is from the KrakenD community; commercial support is available from KrakenD. KRAKEND_URL=https://<public-ip>/ 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/The URL uses the VM's public IP address. krakend-address.service reads it from the Azure Instance Metadata Service at every boot. Step 11 explains why only that small unit may reach the service.
The certificate also names the private IP address. To serve your own DNS name, see Step 10.
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, for the user record and the user's orders, and merges both answers into one response.
From the VM itself, send requests and trust the VM'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 3.0.2 X-Krakend-Completed: true Strict-Transport-Security: max-age=31536000 X-Content-Type-Options: nosniffFrom your own machine, call the gateway over the internet. The certificate is self-signed, so -k skips the check. Step 10 covers your own name and certificate.
curl -k https://<public-ip>/v1/users/2KrakenD only answers the endpoints you declare:
- Any other path returns 404.
- A host name the VM does not serve is dropped.
- An absolute URL that tries to use the gateway as a forward proxy to another host (here the Azure 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 /metadata/instance; 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/metadata/instance?api-version=2021-02-01' -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 /metadata/instance -> 404 unknown host name -> 000 absolute URL for another host -> 000 port 80 -> 000000 means the connection was closed without an answer.
The JWT protected endpoint
/v1/secure/users/{id} is protected by KrakenD's auth/validator. It accepts only an HS256 token signed with this VM'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 -> 200sudo 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, for example your Microsoft Entra ID tenant's keys) instead of a local key. The KrakenD JWT validation documentation covers every option. KrakenD verifies that URL's certificate against the system trust store, which is the store Step 9 checks.
Health and internal endpoints
Load balancers and monitoring can use the public /health probe, for example an Azure Load Balancer or Application Gateway health probe on /health over HTTPS. nginx answers ok only while KrakenD's own /__health reports healthy.
KrakenD's internal endpoints are never reachable from the network. 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/ -> 404If you enable KrakenD's OpenTelemetry exporters later, keep any port they open bound to 127.0.0.1. Never open it in a network security group.
Put your own services behind KrakenD
Edit /etc/krakend/krakend.json and replace the demo endpoints with your own API. The file uses the KrakenD 3.0 configuration format ("version": 4 and the v3.0 schema). KrakenD 3.0 refuses files that still say "version": 3:
sudo jq -c '{"$schema", version, listen_ip, port}' /etc/krakend/krakend.json{"$schema":"https://www.krakend.io/schema/v3.0/krakend.json","version":4,"listen_ip":"127.0.0.1","port":8080}sudo nano /etc/krakend/krakend.jsonEach endpoint lists its backends. A backend can be a service on this VM or a private address in your virtual network. For example, this endpoint 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 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 -nParsing configuration file: /etc/krakend/krakend.json Syntax OK!sudo systemctl restart krakendThe 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 Azure metadata service or the WireServer (Step 11).
To move a 2.x configuration, note these changes in KrakenD 3.0:
- It needs "version": 4.
- It has no Go plugin support in the Community Edition. Lua scripts cover many of the same needs.
- It dropped the OpenCensus, InfluxDB and separate metrics port components in favor of OpenTelemetry.
See KrakenD's upgrade notes and run krakend check --lint on the result. The previous SecureAnyCloud image (KrakenD 2.13) listened on port 8080 in plain HTTP with a public sample backend. This image listens on 443 only.
Check CVE-2026-111943 (compromised eMudhra CAs)
CVE-2026-111943 is a Critical advisory. The private keys of eight eMudhra intermediate certificate authorities were compromised, so anyone holding them could issue a certificate that a vulnerable client trusts for any host name.
KrakenD verifies certificates with Go's crypto/x509 against the operating system trust store (/etc/ssl/certs). It does so for its calls to HTTPS backends, JWK URLs and telemetry exporters, and for client certificates when you enable mTLS. KrakenD Community Edition 3.0.2 is the fixed release.
This image closes the issue twice over. KrakenD is built with Go 1.26.10, whose crypto/x509 rejects any chain through the eight intermediates whatever the trust store says. Ubuntu's ca-certificates update (20260816ubuntu0.24.04.1) also removed the roots those chains end in. The shipped check proves both on your own VM:
sudo krakend-cve-2026-111943-checkcve-2026-111943: ok KrakenD 3.0.2 (>= 3.0.2) cve-2026-111943: ok built with Go 1.26.10 (>= 1.26.10) cve-2026-111943: ok Go's distrust list of the 8 eMudhra intermediates is compiled into /usr/local/bin/krakend cve-2026-111943: ok the running KrakenD (pid <pid>) uses the system trust store cve-2026-111943: ok 120 trusted certificates: no eMudhra intermediate and no root of the affected chains cve-2026-111943: ok positive controls (Amazon Root CA 1, ISRG Root X1) present cve-2026-111943: ok ca-certificates 20260816ubuntu0.24.04.1 (>= 20260816ubuntu0.24.04.1) cve-2026-111943: RESULT CLOSEDThe two positive controls are ordinary public roots that every Ubuntu trust store carries. They prove the check really read the store.
Run the check again after you add your own CA certificates to the system store or replace the KrakenD binary. If you enable mTLS for clients (enable_mtls), KrakenD also trusts the system store for client certificates. To avoid this, set disable_system_ca_pool with an explicit ca_certs list, as KrakenD's advisory recommends.
Your own DNS name and certificate
Point a DNS record at the VM, for example the public IP's Azure DNS label, <label>.<region>.cloudapp.azure.com. Then set the name the gateway serves. This command mints 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 nginxCheck the security posture
The certificate was generated on this VM and names its own addresses. The TLS key and the credentials file are readable only by root, and the JWT signing key is readable 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.txtsubject=CN = <public-ip>, O = sac X509v3 Subject Alternative Name: 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.txtThe 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="<public-ip>" host="127.0.0.1" ua="KrakenD Version 3.0.2"The 500 is expected, because the demo backend has no user called spoof-test.
The Azure metadata block. KrakenD routes requests to addresses written in its configuration, so a mistaken or malicious backend entry must never reach the VM's metadata. That metadata is the Azure Instance Metadata Service (169.254.169.254, which hands out managed identity tokens) and the WireServer (168.63.129.16).
KrakenD, nginx and the first-boot unit carry a systemd IPAddressDeny for both addresses and their IPv6-mapped forms. Only these keep their access:
krakend-address.service, a small sandboxed unit that runs no KrakenD code and only writes this VM's addresses to/run/sac-krakend/address.env.- The Azure Linux agent.
- cloud-init.
for u in krakend nginx krakend-firstboot krakend-address walinuxagent; do printf '%s: ' $u; systemctl show -p IPAddressDeny --value $u.service | tr ' ' '\n' | sort | tr '\n' ' '; echo; done cg=$(systemctl show -p ControlGroup --value krakend.service);
sudo bash -c "echo \$\$ > /sys/fs/cgroup$cg/cgroup.procs; curl -s -m 5 -o /dev/null -w 'inside krakend.service -> Azure IMDS: %{http_code}\n' -H 'Metadata: true' 'http://169.254.169.254/metadata/instance?api-version=2021-02-01'; curl -s -m 5 -o /dev/null -w 'inside krakend.service -> WireServer: %{http_code}\n' 'http://168.63.129.16/?comp=versions'" curl -s -m 5 -o /dev/null -w 'this VM -> Azure IMDS: %{http_code}\n' -H 'Metadata: true' 'http://169.254.169.254/metadata/instance?api-version=2021-02-01'krakend: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 nginx: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 krakend-firstboot: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 krakend-address: walinuxagent: inside krakend.service -> Azure IMDS: 000 inside krakend.service -> WireServer: 000 this VM -> Azure IMDS: 200000 means the connection was refused inside KrakenD's service before it left the VM.
The 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, while ordinary internet and private network backends work as before. As a consequence, KrakenD itself cannot use the VM's managed identity. Give it credentials through its configuration instead.
KrakenD runs as the krakend user with no capabilities and no core dumps. 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 LimitCORE
systemctl cat krakend | grep -E '^(ExecStartPre|Environment)='LimitCORE=0 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 -nSSH allows no root login, no empty passwords and no X11 forwarding (/etc/ssh/sshd_config.d/00-sac.conf). Whether SSH accepts passwords is the choice you made in the Azure portal, key only by default. The image does not override it:
sudo sshd -T | grep -E '^(permitrootlogin|permitemptypasswords|x11forwarding) 'permitrootlogin no x11forwarding no permitemptypasswords noBuild provenance
SecureAnyCloud compiled KrakenD on this image from the unmodified upstream 3.0.2 source. The tagged go.mod and go.sum were used as released, except for one library. golang.org/x/net was raised to v0.62.0, with the four golang.org/x libraries it needs. Two advisories in its HTML package, GO-2026-6860 and GO-2026-6866, were published on 27 October 2026, after KrakenD's release, and govulncheck reports both as reachable in the unmodified build.
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,$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-- BUILD-PROVENANCE.txt -rw-r--r-- LICENSE -rw-r--r-- README.md -rw-r--r-- go.mod -rw-r--r-- go.sum -rw-r--r-- govulncheck-binary.txt a44ee3b23d61ba792177ede1f5c5d0dcff3a68b1fd120dcaffe1ae89b94e21c6 /usr/local/bin/krakend source https://github.com/krakend/krakend-ce/archive/refs/tags/v3.0.2.tar.gz source sha256 8f05f699e3161ec7396011f5eaf0cf4995f0f9f2ff9b925e044e1668d1bdbaac tag commit 0ecf3bf041136d6a6a44e330ebce2c466485f8d0 go.mod / go.sum as tagged (sha256 afeea0f8d4267d5e1ae23aa17039e67c0c72965b6f18688f6cca2ba3a81c2fd6 / 9d321168d67c267a5472c3a6a6a81868d436c1a410e25f369cbbce654f5ec824), then go get golang.org/x/net@v0.62.0: GO-2026-6860 (CVE-2026-78661) and GO-2026-6866 (CVE-2026-97045) in golang.org/x/net/html were published on 2026-10-27, after upstream's release; resulting lines: golang.org/x/crypto v0.58.0, golang.org/x/net v0.62.0, golang.org/x/sync v0.24.0, golang.org/x/sys v0.49.0, golang.org/x/text v0.43.0 resulting go.mod / go.sum sha256 bd6e4934a282a8324776254b42572d4fecc669a09d32a1857827fc298b98ba0a / af707a6d475826b9b99cfbe0ce011b2354a1a3d7ce0fec70ed80c243371a5664 unmodified graph compiles to 46fc12a2ec8eb0f4239bee84c954b328c34094bddf7a6c6c6b5bfbf423a9de5b (not shipped: GO-2026-6860/6866 reachable) schema https://raw.githubusercontent.com/krakend/krakend-schema/0dab76c651fc6be9e578d7cab8e79674e9840a79/v3.0/krakend.json (sha256 8429674ca1ef8d4519079d36099667653546af7300053adb7c4a0f9a7557d9e6; byte-identical to the schema in upstream's 3.0.2 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, the same go get and the same flags (recorded in BUILD-PROVENANCE.txt) with Go 1.26.10 gives the same SHA-256 on any machine. It is a static build (CGO_ENABLED=0). The Go toolchain and module cache were used on a temporary in-memory filesystem and are not on the image.
Logs
KrakenD writes its startup messages and one line per request, including the client address and HTTP method, 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 v3.0.2 [KRAKEND] <time> ▶ INFO [SERVICE: Gin] Listening on port: 8080KrakenD 3.0 also adds the HTTP method to its endpoint log lines, for example [ENDPOINT: GET /v1/users/:id]. Update any log parsing you carried over from KrakenD 2.x.
Server components
Each component and its 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, krakend-address.service, nginx.service
Metadata block: /etc/systemd/system/krakend.service.d/50-sac-imds-deny.conf (and nginx, first boot)
This VM's addresses (every boot): /run/sac-krakend/address.env
Helpers: krakend-set-hostname, krakend-demo-token, krakend-rotate-jwt-key and krakend-cve-2026-111943-check in /usr/local/sbin
License and build provenance: /usr/share/doc/krakend/
VM details: /root/krakend-credentials.txt (mode 0600)
Updating KrakenD
Ubuntu's security updates install automatically, including the ca-certificates trust store KrakenD uses. KrakenD is a single binary that is not managed by apt on this image. KrakenD publishes security fixes for the 3.x line of the Community Edition.
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, then 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 krakendRead the KrakenD release notes and security advisories first. Run sudo krakend-cve-2026-111943-check after a replacement. SecureAnyCloud publishes updated versions of this image as new KrakenD releases ship.
Support
SecureAnyCloud provides 24/7 support for this image, the Ubuntu OS and deployment. Support for KrakenD itself comes from the KrakenD community, and commercial support is available from KrakenD. Contact SecureAnyCloud at 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 Azure
Launch KrakenD Community Edition 3.0.2 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
KrakenD is a high-performance, stateless, declarative open source API gateway written in Go. It sits in front of your backend services and microservices. From a single declarative configuration file, you compose, aggregate, transform, filter, validate, rate-limit, cache and secure your APIs, with no code to write and no database to run.
This image is a ready-to-use API gateway appliance. krakend.service is enabled and starts on boot, serving a working sample gateway configuration. An nginx reverse proxy on port 80 fronts it, so the gateway is already proxying and aggregating traffic when you connect.
What you get:
- A headless, stateless API gateway service. There is no web interface and no credentials to manage.
- One declarative configuration file at
/etc/krakend/krakend.jsonthat describes every endpoint, its upstream backends and the pipeline of middlewares applied to it. - The gateway listening on port 8080, with nginx on port 80 reverse-proxying the public traffic to it. The instance security group opens only port 22 for SSH and port 80 for the gateway, so port 8080 is never exposed to the network and all public gateway traffic arrives through nginx.
- A dedicated, independently resizable EBS data volume mounted at
/var/lib/krakendfor the gateway runtime logs. KrakenD is stateless, so there is no database. The durable per-instance content is the runtime logs plus the configuration that produced them, kept off the operating system disk on durable storage.
Prerequisites
Before you deploy this image, you need:
- An Amazon Web Services account where you can launch EC2 instances.
- IAM permissions to launch instances, create security groups and subscribe to AWS Marketplace products.
- An EC2 key pair in the target Region for SSH access to the instance.
- A VPC and subnet in the target Region. The security group must allow inbound port 22 from your management network and port 80 from your clients.
- The AWS CLI (version 2) installed locally, if you plan to deploy from the command line.
Launch the Instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for KrakenD. Select the SecureAnyCloud listing and choose Select, then Continue on the subscription summary.
Pick an instance type of t3.small or larger. Choose your EC2 key pair under Key pair (login). Under Network settings, select your VPC and subnet. Create or select a security group that opens port 22 from your management network and port 80 from your clients. Leave the root volume at the default size or larger.
Select Launch instance. First boot initialization takes a few seconds after the instance state becomes Running and the status checks pass. krakend.service is already serving its sample gateway configuration behind nginx by the time you can connect.
Launch the Instance from the AWS CLI
The following block launches an instance from the SecureAnyCloud KrakenD Marketplace AMI into an existing subnet and security group. Replace the values as follows:
- <ami-id>: the AMI ID shown on the Marketplace listing.
- <key-name>: your EC2 key pair name.
- <subnet-id>: your subnet ID.
- <security-group-id>: a security group that opens port 22 from your management network and port 80 from your clients.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type t3.small \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=krakend}]'When the instance reaches the Running state and its status checks pass, note its public IP address or DNS name. You can find it in the EC2 console or with aws ec2 describe-instances.
Connect to Your Instance
Connect over SSH using your key pair. For Ubuntu 24.04, the SSH login user is ubuntu.
ssh -i <key-name>.pem ubuntu@<public-ip>A welcome banner prints the most useful commands. sudo cat /root/krakend-info.txt shows the full getting-started notes: what is running, the sample gateway endpoints, where the configuration lives, and how to edit and reload it.
Verify the Service
KrakenD runs as a system service that is enabled and started on boot, with nginx fronting it on port 80. Confirm that the daemon is active:
systemctl is-active krakendThat prints active. For the full unit status, including the main PID, memory use and the command line the daemon runs, use:
systemctl status krakend --no-pagerConfirm the installed binary and version. The version is pinned to KrakenD 2.13.7 on this image:
krakend versionYou should see KrakenD Version: 2.13.7 reported. The krakend binary is installed at /usr/bin/krakend and is on the system path.
The Declarative Configuration
KrakenD is declarative. A single configuration file at /etc/krakend/krakend.json describes every endpoint, its upstream backends and the middleware pipeline applied to it. Validate the configuration at any time with the built-in checker:
sudo krakend check -c /etc/krakend/krakend.jsonThat prints Syntax OK! when the configuration is valid. To view the endpoints and their backends as a summary, run:
jq '{name, port, endpoints: [.endpoints[] | {endpoint, method, backends: [.backend[].url_pattern]}]}' /etc/krakend/krakend.jsonThe shipped sample configuration defines three endpoints that demonstrate the gateway proxying and aggregating out of the box:
- A
/healthendpoint that passes through KrakenD's own health check. - A
/combo/userendpoint that aggregates two upstream JSON backends into one document. - A
/catalog/productsendpoint that proxies a single upstream backend.
Call the Gateway
The gateway is reachable through nginx on port 80. KrakenD also exposes its own built-in health endpoint at /__health. It returns {"status":"ok"} with HTTP 200 while the gateway is healthy. From the instance itself, you can call it on loopback:
curl -s http://127.0.0.1/__healthFrom any client, replace 127.0.0.1 with the instance public IP.
The /combo/user endpoint is an aggregation. KrakenD fans out to two upstream backends in parallel, a user record and that user's posts. It merges the two responses into a single document under a profile group and a posts group.
curl -s http://<public-ip>/combo/userThe /catalog/products endpoint is a single-backend proxy that fetches an upstream products API and returns a filtered view of it:
curl -s http://<public-ip>/catalog/productsThese three calls confirm that the gateway is serving, aggregating multiple backends and proxying upstream APIs.
Edit the Gateway
To build your own gateway, edit the single declarative configuration file and point the backends at your own services. KrakenD supports the following features, all declared in the same file with no code:
- Response aggregation across many backends.
- Request and response transformation.
- Response filtering to allow or deny fields.
- Rate limiting at the endpoint and per-user level.
- JWT validation and signing, and role-based access control.
- CORS and security headers.
- In-memory caching, circuit breakers and timeouts.
Edit /etc/krakend/krakend.json. Always validate before reloading, then restart the service to apply your changes. These commands edit and reload your gateway:
sudo krakend check -c /etc/krakend/krakend.json
sudo systemctl restart krakendBecause the gateway is stateless, you can run several instances behind a load balancer with no shared state, which scales the gateway tier horizontally. Open whatever additional ports your endpoints need in the instance security group. For production traffic, put a TLS terminator in front of the gateway, such as an Application Load Balancer or your own certificate on nginx.
The Data Volume
The gateway runtime logs live on a dedicated EBS volume mounted at /var/lib/krakend. KrakenD is stateless, so this volume holds the durable per-instance content, the runtime logs, off the operating system disk. You can resize or snapshot it independently. Confirm the mount with:
df -h /var/lib/krakendThe krakend.service working directory is set to this volume, so any relative log paths your configuration uses land on the data disk rather than the root disk.
To grow the volume, expand the EBS volume in the AWS console. Then grow the filesystem on the instance with sudo resize2fs on the underlying device.
Support
SecureAnyCloud publishes and supports this image. Support covers endpoint and backend configuration, aggregation and response manipulation, rate limiting and security policies, JWT and authentication, caching and resilience, observability and upgrade planning. Contact SecureAnyCloud through the support channel listed on the AWS Marketplace listing.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
Deploy on AWS
Launch KrakenD on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com