This article is a continuation of retrying dynamically configured upstreams that gives an example of how you can configure OpenResty to update your upstream backend servers dynamically with DNS.

Breaking down init_worker_by_lua_block

init_worker_by_lua_block can be used to make an nginx worker do some fun stuff. In this instance we’re going to use it in conjunction with ngx.timer.every and the resty.dns.resolver.

Here is the full example of my init_worker_by_lua_block.

init_worker_by_lua_block {
    _backend_servers = {}

    local function update_dns()
        -- Set up the resolver
        local resolver = require "resty.dns.resolver"
        local r, err = resolver:new{
            nameservers = {"", {"", 53} },  -- Cloudflare
            retrans = 5,  -- 5 retransmissions on receive timeout
            timeout = 1000,  -- 1 sec
        if not r then
            ngx.log(ngx.ERR, "failed to instantiate resolver: ", err)

        -- Pull DNS records
        -- Use a hardcoded domain to make this example easier
        local answers, err, tries = r:query(" …


OpenResty is a modified version of nginx with LuaJIT compiled in and many nginx options that can be controlled or modified via Lua. It is very commonly used in content delivery networks for it’s configurability.

As such, we use OpenResty and one of the features we use is the ability to dynamically modify upstream backends. To achieve this we use some logic within OpenResty to update upstreams based on DNS records. This means we can pull upstreams in and out of service via DNS records and have OpenResty update it’s upstream proxy passing configuration without needing to push configs out to hundreds of servers and reload daemons.

The logic behind how we update the upstream backends is beyond the scope of this post, so let’s just say we have a table of upstream servers.

local upstream_servers = {
    " …