Integrate with Netflix Eureka
Configure Apache APISIX to discover two controlled local services through Netflix Eureka, validate routing, and restore the original configuration.
Applies to:Apache APISIX 3.18.0
Deployments:DockerRoles:Platform engineerTime:25 minutesImpact:Changes live traffic
Goal
Register two disposable web services in Eureka and configure APISIX to discover them dynamically. The procedure isolates every container on the APISIX quickstart network, validates both endpoints, and restores the exact pre-guide APISIX configuration.
Before you begin
- Complete Get APISIX with the Docker quickstart.
- Install Docker, cURL, and jq.
- Use a shell that supports Bash or Zsh heredocs.
- The community
bitinit/eurekaimage is suitable only for this isolated local evaluation. Use an approved, maintained Eureka deployment with authentication and TLS in production.
Set the endpoints and read the Admin API key without placing it in shell history:
export APISIX_ADMIN_URL="http://127.0.0.1:9180"
export GATEWAY_URL="http://127.0.0.1:9080"
export GUIDE_DIR="$(pwd)"
read -rsp "Admin API key (press Enter for an unchanged quickstart): " APISIX_ADMIN_KEY && export APISIX_ADMIN_KEY && echoProtect the fixed Route, containers, and local files used by this guide:
test "$(curl -sS --connect-timeout 3 --max-time 10 -o /dev/null -w '%{http_code}' "${APISIX_ADMIN_URL}/apisix/admin/routes/eureka-guide-route" -H "X-API-KEY: ${APISIX_ADMIN_KEY}")" = 404 || { echo 'Stop: route eureka-guide-route already exists'; exit 1; }
for container in eureka-guide-registry eureka-guide-web-1 eureka-guide-web-2; do
test -z "$(docker ps -aq --filter "name=^/${container}$")" || { echo "Stop: container ${container} already exists"; exit 1; }
done
for file in eureka-guide-nginx-1.conf eureka-guide-nginx-2.conf eureka-guide-config.yaml.backup eureka-guide-config.yaml.updated eureka-guide-responses.txt; do
test ! -e "${file}" || { echo "Stop: ${file} already exists"; exit 1; }
doneSteps
Step 1: Start a controlled Eureka registry
Start a single-node Eureka server on the quickstart network without publishing its port:
docker run -d --name eureka-guide-registry \
--network apisix-quickstart-net \
-e ENVIRONMENT=apisix \
-e spring.application.name=apisix-eureka \
-e server.port=8761 \
-e eureka.instance.ip-address=127.0.0.1 \
-e eureka.client.registerWithEureka=true \
-e eureka.client.fetchRegistry=false \
-e eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/ \
bitinit/eureka:latest
for attempt in {1..60}; do
docker run --rm --network apisix-quickstart-net curlimages/curl:8.16.0 \
--fail --silent --show-error --connect-timeout 2 --max-time 5 \
-H 'Accept: application/json' http://eureka-guide-registry:8761/eureka/apps >/dev/null && break
sleep 1
done
docker run --rm --network apisix-quickstart-net curlimages/curl:8.16.0 \
--fail --silent --show-error --connect-timeout 2 --max-time 5 \
-H 'Accept: application/json' http://eureka-guide-registry:8761/eureka/apps >/dev/nullWhat changed: a disposable Eureka server is ready only on apisix-quickstart-net.
Step 2: Start and register two web services
Create two minimal NGINX configurations and start the services without publishing their ports:
cat > eureka-guide-nginx-1.conf <<'NGINX'
events {}
http {
access_log off;
server {
listen 80;
location = / { return 200 "eureka-guide-web-1"; }
}
}
NGINX
cat > eureka-guide-nginx-2.conf <<'NGINX'
events {}
http {
access_log off;
server {
listen 80;
location = / { return 200 "eureka-guide-web-2"; }
}
}
NGINX
docker run -d --name eureka-guide-web-1 --network apisix-quickstart-net \
-v "${GUIDE_DIR}/eureka-guide-nginx-1.conf:/etc/nginx/nginx.conf:ro" nginx:1.29.1-alpine
docker run -d --name eureka-guide-web-2 --network apisix-quickstart-net \
-v "${GUIDE_DIR}/eureka-guide-nginx-2.conf:/etc/nginx/nginx.conf:ro" nginx:1.29.1-alpine
for container in eureka-guide-web-1 eureka-guide-web-2; do
for attempt in {1..15}; do
docker exec apisix-quickstart bash -c "</dev/tcp/${container}/80" >/dev/null 2>&1 && break
sleep 1
done
docker exec apisix-quickstart bash -c "</dev/tcp/${container}/80"
doneCapture each private address and register two UP instances under the same application name:
for number in 1 2; do
jq -n --arg id "eureka-guide-web-${number}" \
--arg ip "$(docker inspect "eureka-guide-web-${number}" | jq -r '.[0].NetworkSettings.Networks["apisix-quickstart-net"].IPAddress')" \
'{instance:{instanceId:$id,hostName:$id,ipAddr:$ip,port:{"$":80,"@enabled":true},status:"UP",app:"EUREKA-GUIDE-WEB",dataCenterInfo:{name:"MyOwn","@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"}}}' | \
docker run --rm -i --network apisix-quickstart-net curlimages/curl:8.16.0 \
--fail --silent --show-error --connect-timeout 2 --max-time 10 \
-X POST http://eureka-guide-registry:8761/eureka/apps/EUREKA-GUIDE-WEB \
-H 'Content-Type: application/json' --data-binary @-
done
docker run --rm --network apisix-quickstart-net curlimages/curl:8.16.0 \
--fail --silent --show-error --connect-timeout 2 --max-time 10 \
-H 'Accept: application/json' http://eureka-guide-registry:8761/eureka/apps/EUREKA-GUIDE-WEB | \
jq -e '([.application.instance[].instanceId] | sort) == ["eureka-guide-web-1","eureka-guide-web-2"] and all(.application.instance[]; .status == "UP")'What changed: Eureka resolves EUREKA-GUIDE-WEB to exactly the two private container addresses.
Step 3: Enable Eureka discovery in APISIX
Back up the complete APISIX configuration. Stop if it already contains a top-level discovery block, because that configuration requires a reviewed merge:
umask 077
docker cp apisix-quickstart:/usr/local/apisix/conf/config.yaml eureka-guide-config.yaml.backup
test "$(grep -Ec '^discovery:[[:space:]]*$' eureka-guide-config.yaml.backup)" = 0 || { echo 'Stop: merge with the existing top-level discovery configuration'; exit 1; }
cp eureka-guide-config.yaml.backup eureka-guide-config.yaml.updated
cat >> eureka-guide-config.yaml.updated <<'YAML'
discovery:
eureka:
host:
- "http://eureka-guide-registry:8761"
prefix: /eureka/
fetch_interval: 5
weight: 100
timeout:
connect: 2000
send: 2000
read: 5000
YAML
docker cp eureka-guide-config.yaml.updated apisix-quickstart:/usr/local/apisix/conf/config.yaml
if ! docker exec apisix-quickstart apisix test; then
docker cp eureka-guide-config.yaml.backup apisix-quickstart:/usr/local/apisix/conf/config.yaml
docker exec apisix-quickstart apisix reload
exit 1
fi
docker exec apisix-quickstart apisix reloadWhat changed: APISIX uses the local Eureka endpoint while retaining every pre-existing configuration field.
Step 4: Create the discovery Route
Create one authenticated fixed-ID Route and assert the stored discovery settings:
curl -sS --connect-timeout 3 --max-time 15 \
"${APISIX_ADMIN_URL}/apisix/admin/routes/eureka-guide-route" -X PUT \
-H "X-API-KEY: ${APISIX_ADMIN_KEY}" -H 'Content-Type: application/json' \
-d '{
"name":"eureka-guide-route",
"uri":"/eureka-guide",
"methods":["GET"],
"upstream":{
"service_name":"EUREKA-GUIDE-WEB",
"discovery_type":"eureka",
"type":"roundrobin"
}
}' | jq -e '.value.id == "eureka-guide-route" and .value.upstream.service_name == "EUREKA-GUIDE-WEB" and .value.upstream.discovery_type == "eureka"'What changed: GET /eureka-guide selects nodes discovered from the controlled Eureka application.
Validate
Allow discovery to converge, then require successful responses from both registered endpoints:
for attempt in {1..30}; do
: > eureka-guide-responses.txt
for request in {1..8}; do
curl --fail-with-body -sS --connect-timeout 3 --max-time 10 "${GATEWAY_URL}/eureka-guide"
echo
done > eureka-guide-responses.txt
test "$(sort -u eureka-guide-responses.txt | tr '\n' ' ')" = 'eureka-guide-web-1 eureka-guide-web-2 ' && break
sleep 1
done
test "$(sort -u eureka-guide-responses.txt | tr '\n' ' ')" = 'eureka-guide-web-1 eureka-guide-web-2 'The final assertion proves that APISIX reached both nodes returned for the same Eureka application.
Roll back / Clean up
Delete the Route, deregister the instances, restore the exact APISIX configuration, and remove all disposable resources:
curl --fail-with-body -sS --connect-timeout 3 --max-time 15 -X DELETE \
"${APISIX_ADMIN_URL}/apisix/admin/routes/eureka-guide-route" \
-H "X-API-KEY: ${APISIX_ADMIN_KEY}"
for number in 1 2; do
docker run --rm --network apisix-quickstart-net curlimages/curl:8.16.0 \
--fail --silent --show-error --connect-timeout 2 --max-time 10 \
-X DELETE "http://eureka-guide-registry:8761/eureka/apps/EUREKA-GUIDE-WEB/eureka-guide-web-${number}"
done
docker cp eureka-guide-config.yaml.backup apisix-quickstart:/usr/local/apisix/conf/config.yaml
docker exec apisix-quickstart apisix test
docker exec apisix-quickstart apisix reload
docker rm -f eureka-guide-web-1 eureka-guide-web-2 eureka-guide-registry
rm -f eureka-guide-nginx-1.conf eureka-guide-nginx-2.conf eureka-guide-config.yaml.backup \
eureka-guide-config.yaml.updated eureka-guide-responses.txt
unset APISIX_ADMIN_URL APISIX_ADMIN_KEY GATEWAY_URL GUIDE_DIRExpected: the fixed Route, two registrations, three containers, and local files are absent, and APISIX is running with the byte-for-byte pre-guide configuration.
Troubleshoot
| Symptom | Cause | Fix |
|---|---|---|
The configuration guard stops at discovery. | APISIX already has service discovery configuration. | Do not append another top-level key. Merge eureka into the existing discovery mapping in a reviewed change and retain the exact backup. |
apisix test fails. | The updated YAML is invalid or incompatible with the running APISIX image. | Keep the automatic restoration, inspect the test output, and do not reload the rejected file. |
| The application lookup returns a non-JSON response. | Eureka is still starting or the request did not ask for JSON. | Re-run the readiness request with Accept: application/json before registering instances. |
The Route returns 503 or 502. | Discovery has not converged, a registration is absent, or a private container address changed. | Re-register both current addresses and verify that all containers share apisix-quickstart-net. |
| Validation sees only one response. | One NGINX container is unavailable or Eureka exposes only one UP instance. | Check both TCP readiness tests and the exact instance assertion before increasing the retry loop. |
Related
- APISIX service discovery — Eureka configuration and upstream fields.
- Upstreams and service discovery — APISIX upstream concepts.
- Discovery Control API — inspect discovery data when troubleshooting.