How to Auto‑Update Nginx Upstreams with Nacos Service Discovery (Lua & Java Agents)
Learn how to replace manual Nginx upstream configuration with automatic service discovery using Nacos, by either writing Lua scripts with the nginx‑lua‑module or building a Java/Golang agent that fetches instance lists via the Nacos API, updates upstream blocks, and triggers hot reloads.
Background
Traditionally, Nginx load‑balancing is configured manually by defining an upstream block with static server IP:PORT entries and reloading Nginx whenever the service list changes.
upstream serverList {
server 172.17.0.111:9999;
server 172.17.0.110:9999;
}
server {
location / {
proxy_pass http://serverList;
}
}In a micro‑service architecture, instances are registered in a service registry such as Nacos or Eureka, which already maintains the IP:PORT list. The goal is to let Nginx obtain this list automatically and update its upstream definition with hot reload.
Two Implementation Approaches
Use the nginx‑lua‑module to write a Lua script that calls the registry’s HTTP API, generates the upstream configuration, and triggers a timed reload for hot updates.
Write a separate agent in Java or Golang that uses the Nacos SDK to subscribe to service changes, generates the upstream block, and invokes nginx -s reload when instances appear or disappear.
nacos‑nginx‑template
The second approach is packaged as the nacos‑nginx‑template project, which runs an agent that watches Nacos and rewrites Nginx configuration.
Download binary
Configure config.toml
The configuration file uses TOML syntax. Example:
nginx_cmd = "/usr/sbin/nginx" # full path to nginx binary
nacos_addr = "172.16.0.100:8848" # Nacos address
reload_interval = 1000 # refresh interval in ms
[discover_config1]
nginx_config = "/etc/nginx/nginx.conf"
nginx_upstream = "upsteam1"
nacos_service_name = "service1"
[discover_config2]
nginx_config = "/etc/nginx/nginx.conf"
nginx_upstream = "upsteam2"
nacos_service_name = "service2"Start the agent
sh bin/startup.shCore Java Code
Read config.toml, subscribe to Nacos services, and refresh upstream blocks.
for (DiscoverConfigBO configBO : list) {
namingService.subscribe(configBO.getServiceName(),
event -> {
List<Instance> instances = namingService.getAllInstances(configBO.getServiceName());
// update nginx upstream
refreshUpstream(instances, configBO.getUpstream(), configBO.getConfigPath());
});
} private boolean refreshUpstream(List<Instance> instances, String nginxUpstream, String nginxConfigPath) {
Pattern pattern = Pattern.compile(UPSTREAM_REG.replace(PLACEHOLDER, nginxUpstream));
String conf = FileUtl.readStr(nginxConfigPath);
String newUpstream = UPSTREAM_FOMAT.replace(PLACEHOLDER, nginxUpstream);
StringBuffer servers = new StringBuffer();
if (instances.size() > 0) {
for (Instance instance : instances) {
if (!instance.isHealthy() || !instance.isEnabled()) {
continue;
}
servers.append(formatSymbol + " server " + instance.getIp() + ":" + instance.getPort() + ";
");
}
}
servers.append(formatSymbol);
newUpstream = newUpstream.replace(PLACEHOLDER_SERVER, servers.toString());
conf = matcher.replaceAll(newUpstream);
return true;
}Reload Nginx from Java:
Runtime.getRuntime().exec("nginx -s reload");Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
