Skip to content
30 changes: 30 additions & 0 deletions doc/configuration.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5943,6 +5943,7 @@ option pgsql-check X - X X
option prefer-last-server (*) X - X X
option redispatch (*) X - X X
option redis-check X - X X
option server-rename - - X X
option smtpchk X - X X
option socket-stats (*) X X X -
option splice-auto (*) X X X X
Expand Down Expand Up @@ -6031,6 +6032,35 @@ use-server - - X X


4.2. Alphabetically sorted keywords reference

option server-rename
Enable runtime renaming of servers via the CLI.

May be used in the following contexts: tcp, http

May be used in sections : defaults | frontend | listen | backend
no | no | no | yes

When this option is enabled in a backend, it allows the name of any server in that backend to be changed at runtime using the CLI command:
set server <backend>/<server> name <newname>

Restrictions:
- The new name must be unique among all servers in the backend.
- The change is effective immediately for all runtime operations, stats, and logs.
- The new name is not persisted to the configuration file and will be lost on reload.
- Renaming is only available if 'option server-rename' is set in the backend.
- Some features that rely on static server names (such as certain stick-tables or external monitoring) may not recognize the new name until restart.

Example:
backend myapp
option server-rename
server s1 10.0.0.1:80
server s2 10.0.0.2:80

To rename server 's1' to 'blue':
echo "set server myapp/s1 name blue" | socat /var/run/haproxy.sock -

See also: 'set server <b>/<s> name' in the management guide (doc/management.txt).
---------------------------------------------

This section provides a description of each keyword and its usage.
Expand Down
30 changes: 30 additions & 0 deletions doc/management.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2663,6 +2663,36 @@ set server <backend>/<server> ssl [ on | off ] (deprecated)
This command is deprecated, create a new server dynamically with or without
SSL instead, using the "add server" command.

set server <backend>/<server> name <newname>
Change the name of a server at runtime (if 'option server-rename'
is enabled in the backend).

The new name must be unique among all servers in the backend. The
change is effective immediately for all runtime operations, stats,
and logs, but is not persisted to the configuration file and will
be lost on reload. Some features that rely on static server names
(such as stick-tables or external monitoring) may not recognize
the new name until restart.

There is no strict requirement that no sessions are active
before renaming. However, if there are active sessions, log entries
for those sessions may use either the old or new name, depending on
when the rename occurs relative to logging. This may result in split
or inconsistent logs for in-flight requests. If strict log consistency
is required, it is the user's responsibility to ensure all sessions
have finished before renaming (e.g., by draining or maintenance mode
and waiting for all connections to close).

For most use cases, a clear naming scheme (such as srv1 → srv1-old,
srv1-new, or srv1*) allows correlating log entries before and after
the rename, even if some overlap occurs. Users should choose a naming
convention that fits their operational and troubleshooting needs.

Example:
set server myapp/s1 name blue

See also: 'option server-rename' in doc/configuration.txt.

set severity-output [ none | number | string ]
Change the severity output format of the stats socket connected to for the
duration of the current session.
Expand Down
2 changes: 2 additions & 0 deletions include/haproxy/event_hdl-t.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ struct event_hdl_sub {
#define EVENT_HDL_SUB_SERVER_CHECK EVENT_HDL_SUB_TYPE(1,7)
/* server inet addr (addr:svc_port tuple) change event */
#define EVENT_HDL_SUB_SERVER_INETADDR EVENT_HDL_SUB_TYPE(1,8)
/* server name change event */
#define EVENT_HDL_SUB_SERVER_NAME EVENT_HDL_SUB_TYPE(1,9)

/* PAT_REF family, only published in pat ref subscription list
* (not published in global subscription list for performance reasons)
Expand Down
4 changes: 3 additions & 1 deletion include/haproxy/proxy-t.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ enum PR_SRV_STATE_FILE {
#define PR_O3_HASHAFNTY_MAXQUEUE 0x00000200 /* preserve hash affinity until maxqueue is reached */
#define PR_O3_HASHAFNTY_MASK 0x00000300 /* mask for hash-preserve-affinity */

/* unused: 0x00000400 to 0x80000000 */
#define PR_O3_SRV_RENAME 0x00000400 /* allow runtime server renaming */

/* unused: 0x00000800 to 0x80000000 */
/* end of proxy->options3 */

/* Cookie settings for pr->ck_opts */
Expand Down
19 changes: 19 additions & 0 deletions include/haproxy/server-t.h
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ struct event_hdl_cb_data_server {
* EVENT_HDL_SUB_SERVER_ADMIN
* EVENT_HDL_SUB_SERVER_CHECK
* EVENT_HDL_SUB_SERVER_INETADDR
* EVENT_HDL_SUB_SERVER_NAME
*/
struct {
/* safe data can be safely used from both
Expand Down Expand Up @@ -768,6 +769,24 @@ struct event_hdl_cb_data_server_inetaddr {
/* no unsafe data */
};

/* data provided to EVENT_HDL_SUB_SERVER_NAME handlers through
* event_hdl facility
*
* Note that this may be casted to regular event_hdl_cb_data_server if
* you don't care about name related optional info
*/
struct event_hdl_cb_data_server_name {
/* provided by:
* EVENT_HDL_SUB_SERVER_NAME
*/
struct event_hdl_cb_data_server server; /* must be at the beginning */
struct {
char old_name[64];
char new_name[64];
} safe;
/* no unsafe data */
};

/* Storage structure to load server-state lines from a flat file into
* an ebtree, for faster processing
*/
Expand Down
1 change: 1 addition & 0 deletions src/proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ const struct cfg_opt cfg_opts3[] =
{
{"assume-rfc6587-ntf", PR_O3_ASSUME_RFC6587_NTF, PR_CAP_FE, 0, PR_MODE_SYSLOG },
{"dont-parse-log", PR_O3_DONTPARSELOG, PR_CAP_FE, 0, PR_MODE_SYSLOG },
{"server-rename", PR_O3_SRV_RENAME, PR_CAP_BE, 0, 0 },
{ NULL, 0, 0, 0 }
};

Expand Down
131 changes: 128 additions & 3 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
#include <haproxy/tools.h>
#include <haproxy/xxhash.h>
#include <haproxy/event_hdl.h>
#include <haproxy/stick_table-t.h>
#include <haproxy/peers-t.h>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imports are sorted


static void srv_update_status(struct server *s, int type, int cause);
static int srv_apply_lastaddr(struct server *srv, int *err_code);
Expand Down Expand Up @@ -5397,6 +5399,114 @@ const char *srv_update_fqdn(struct server *server, const char *fqdn, const char
}


/* Rename a server at runtime. This function handles all precondition checks,
* tree re-indexing under thread_isolate(), and event publication.
*
* The caller must NOT hold any lock — this function uses thread_isolate()
* internally for tree manipulation.
*
* Preconditions enforced:
* - backend must have "option server-rename" set
* - server must be administratively in maintenance
* - new name must not conflict with an existing server in the backend
* - new name must be syntactically valid (no spaces, '/', '#' prefix)
* - backend must not have sticking rules referencing a peer-synced table
*
* Returns NULL on success, or a pointer to a static/trash error message
* string on failure. On success, a ha_notice() is emitted and the
* EVENT_HDL_SUB_SERVER_NAME event is published.
*/
static const char *srv_update_server_name(struct server *srv, const char *new_name)
{
struct proxy *be = srv->proxy;
struct sticking_rule *rule;
char *old_name;
char *dup;
const char *p;

/* validate the new name is not empty */
if (!*new_name)
return "Require a new server name.\n";

/* reject names starting with '#' (numeric ID syntax in server_find) */
if (*new_name == '#')
return "Server name must not start with '#'.\n";

/* reject control characters, spaces, and '/' which break CLI parsing */
for (p = new_name; *p; p++) {
if ((unsigned char)*p <= ' ' || *p == '/')
return "Server name must not contain spaces, control characters, or '/'.\n";
}
Comment on lines +5517 to +5521
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no utility function for this task?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I searched again, but I couldn't find any helper like that sadly.


/* per-backend opt-in required */
if (!(be->options3 & PR_O3_SRV_RENAME))
return "Backend does not allow server renaming (add 'option server-rename').\n";

/* server must be administratively down (in maintenance) */
if (!(srv->cur_admin & SRV_ADMF_MAINT))
return "Server must be in maintenance mode to be renamed (set server <b>/<s> state maint).\n";

/* reject if any sticking rule references a peer-synced table */
list_for_each_entry(rule, &be->sticking_rules, list) {
if (rule->table.t && rule->table.t->peers.p)
return "Cannot rename: backend has sticking rules using a peer-synced table.\n";
}
list_for_each_entry(rule, &be->storersp_rules, list) {
if (rule->table.t && rule->table.t->peers.p)
return "Cannot rename: backend has store-response rules using a peer-synced table.\n";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Length of new server name is not checked.

/* same name is a no-op success */
if (strcmp(srv->id, new_name) == 0)
return NULL;

/* allocate new name before taking isolation */
dup = strdup(new_name);
if (!dup)
return "Out of memory allocating new server name.\n";

/* tree manipulation requires thread isolation (same pattern as
* add/del server). This is rare enough that the cost is acceptable.
*/
thread_isolate();

/* re-check for name conflict under isolation — another rename or
* add server could have raced before we isolated.
*/
if (server_find_by_name(be, new_name)) {
thread_release();
free(dup);
return "A server with the same name already exists in this backend.\n";
}

/* --- point of no return --- */

old_name = srv->id;

/* re-index in the name tree */
cebis_item_delete(&be->conf.used_server_name, conf.name_node, id, srv);
srv->id = dup;
cebis_item_insert(&be->conf.used_server_name, conf.name_node, id, srv);

/* publish rename event with both old and new names */
{
struct event_hdl_cb_data_server_name cb_data;

_srv_event_hdl_prepare(&cb_data.server, srv, 1);
snprintf(cb_data.safe.old_name, sizeof(cb_data.safe.old_name), "%s", old_name);
snprintf(cb_data.safe.new_name, sizeof(cb_data.safe.new_name), "%s", new_name);
_srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_NAME, cb_data, srv);
}

thread_release();

ha_notice("Server %s/%s renamed from '%s'.\n", be->id, srv->id, old_name);
free(old_name);

return NULL;
}


/* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
* and returns the pointer to the server. Otherwise, display adequate error messages
* on the CLI, sets the CLI's state to CLI_ST_PRINT and returns NULL. This is only
Expand Down Expand Up @@ -5654,11 +5764,26 @@ static int cli_parse_set_server(char **args, char *payload, struct appctx *appct
#else
cli_msg(appctx, LOG_NOTICE, "server ssl setting not supported.\n");
#endif
} else {
}
else if (strcmp(args[3], "name") == 0) {
if (!*args[4]) {
cli_err(appctx, "set server <b>/<s> name requires a new name.\n");
goto out;
}
/* srv_update_server_name() handles its own locking via
* thread_isolate(), so do NOT hold the server lock here.
*/
warning = srv_update_server_name(sv, args[4]);
if (warning)
cli_err(appctx, warning);
else
cli_msg(appctx, LOG_NOTICE, "Server name updated.\n");
}
else {
cli_err(appctx,
"usage: set server <backend>/<server> "
"addr | agent | agent-addr | agent-port | agent-send | "
"check-addr | check-port | fqdn | health | ssl | "
"check-addr | check-port | fqdn | health | name | ssl | "
"state | weight\n");
}
out:
Expand Down Expand Up @@ -6533,7 +6658,7 @@ static struct cli_kw_list cli_kws = {{ },{
{ { "enable", "health", NULL }, "enable health : enable health checks", cli_parse_enable_health, NULL },
{ { "enable", "server", NULL }, "enable server (DEPRECATED) : enable a disabled server (use 'set server' instead)", cli_parse_enable_server, NULL },
{ { "set", "maxconn", "server", NULL }, "set maxconn server <bk>/<srv> : change a server's maxconn setting", cli_parse_set_maxconn_server, NULL },
{ { "set", "server", NULL }, "set server <bk>/<srv> [opts] : change a server's state, weight, address or ssl", cli_parse_set_server },
{ { "set", "server", NULL }, "set server <bk>/<srv> [opts] : change a server's state, weight, address, name or ssl", cli_parse_set_server },
{ { "get", "weight", NULL }, "get weight <bk>/<srv> : report a server's current weight", cli_parse_get_weight },
{ { "set", "weight", NULL }, "set weight <bk>/<srv> (DEPRECATED) : change a server's weight (use 'set server' instead)", cli_parse_set_weight },
{ { "add", "server", NULL }, "add server <bk>/<srv> : create a new server", cli_parse_add_server, cli_io_handler_add_server },
Expand Down
Loading