Announcing HAProxy Data Plane API 2.7
Version Update

HAProxy Technologies is proud to unveil the 2.7 release of HAProxy Data Plane API. This release was a huge undertaking, and as with the Data Plane API 2.6, we focused on extending support for configuration keywords. We are happy to announce that with this release we support all HAProxy configuration keywords in the Data Plane API. Along with enhanced keyword coverage, we’ve added the ability to specify multiple named default sections through the API.

In addition, we’re excited to announce some quality of life improvements. You can now add and delete servers dynamically using the Runtime API without the need to reload, and we’ve added reload strategies to simplify the configuration of the Data Plane API.

Extended HAProxy configuration keywords support

image4

Added support for sections: program, fcgi-app, mailers and http-errors

To complete the goal of covering the entire configuration in the Data Plane API, we needed to add support for sections that were missing. Although not used as often as other sections like backend, frontend, and others, they can be very useful in some use cases.

program section

The program section defines external binaries. When running HAProxy in master-worker mode, which you do by adding the -W flag when starting HAProxy, it is possible to launch external binaries with the master process. These external binaries are called programs. They are launched and managed the same way as the HAProxy worker processes, but could perform other long-running tasks like hosting the HAProxy traffic mirroring program, as shown in the example below.

To create a new program section through the API, use the /v2/services/haproxy/configuration/programs resource:

$ curl -X POST \
-–user admin:password \
-H "Content-Type: application/json" \
-d '{
"name": "mirror",
"command": "spoa-mirror --runtime 0 --mirror-url http://test.local",
"user": "myusername",
"group": "mygroupname",
"start-on-reload": "enabled"
}' \
"http://127.0.0.1:5555/v2/services/haproxy/configuration/programs?version=1"

This request creates the following program section in the HAProxy configuration file:

program mirror
command spoa-mirror --runtime 0 --mirror-url http://test.local
user myusername
group mygroupname
option start-on-reload

fcgi-app section

A fcgi-app section defines how HAProxy should connect to a web application using the FastCGI protocol. Once you’ve defined the section, you can use it in backend sections with the use-fcgi-app keyword. We covered HAProxy’s support for FastCGI in our blog post Load Balancing PHP-FPM with HAProxy and FastCGI.

You can create a new fcgi-app section through the API using the newly added resource /v2/services/haproxy/configuration/fcgi_apps:

$ curl -X POST \
-–user admin:password \
-H "Content-Type: application/json" \
-d '{
"name": "php_fpm",
"docroot": "/var/www/myapp",
"index": "index.php",
"path-info": "^(/.+\\.php)(/.*)?$",
"log-stderr": [
{
"global": true
}
]
}' \
"http://127.0.0.1:5555/v2/services/haproxy/configuration/fcgi_apps?version=2"

This request creates an fcgi-app section in your HAProxy configuration file that looks like this:

fcgi-app php_fpm
docroot /var/www/myapp
index index.php
path-info ^(/.+\.php)(/.*)?$
log-stderr global

To activate FastCGI, you must then reference this fcgi-app section in a backend, which you can do by making a PUT request to the  /services/haproxy/configuration/backends/{name} resource with the use-fcgi-app parameter in the body of the request.

mailers section

A mailers section lists mail servers to which HAProxy will send notification emails via the SMTP protocol. To create a mailers section, use /v2/services/haproxy/configuration/mailers_section:

$ curl -X POST \
-–user admin:password \
-H "Content-Type: application/json" \
-d '{
"name": "mymailers",
"timeout": 20000
}' \
"http://127.0.0.1:5555/v2/services/haproxy/configuration/mailers_sections?version=4"

Then to define the servers, add mailer lines to the mailers section using the /v2/services/haproxy/configuration/mailer_entry resource:

$ curl -X POST \
-–user admin:password \
-H "Content-Type: application/json" \
-d '{
"name": "mymailers",
"timeout": 20000
}' \
"http://127.0.0.1:5555/v2/services/haproxy/configuration/mailers_sections?version=4"

These requests create an HAProxy configuration like this:

mailers mymailers
timeout mail 20000

http-errors section

By adding an http-errors section to your configuration, you can define branded error pages to display to users rather than the basic, stock error pages. We cover this feature in our blog post Serve Dynamic Custom Error Pages with HAProxy. It is possible to globally declare several groups of HTTP error pages, which can be imported afterwards into any proxy section. The same group may be referenced at multiple places and can be fully or partially imported.

To create a new http-errors section using the API you can use the /v2/services/haproxy/configuration/http_errors_sections resource:

$ curl -X POST \
-–user admin:password \
-H "Content-Type: application/json" \
-d '{
"name": "website-1",
"error_files": [
{
"code": 400,
"name": "/etc/haproxy/errorfiles/site1/400.http"
},
{
"code": 404,
"name": "/etc/haproxy/errorfiles/site1/404.http"
}
]
}' \
"http://127.0.0.1:5555/v2/services/haproxy/configuration/http_errors_sections?version=3"

This request creates an HAProxy configuration like this:

http-errors website-1
errorfile 400 /etc/haproxy/errorfiles/site1/400.http
errorfile 404 /etc/haproxy/errorfiles/site1/404.http
mailer smtp1 192.168.0.1:587

New resource http_error_rules

In addition to those new sections, there is a new resource, /services/haproxy/configuration/http_error_rules, which represents http-error in the HAProxy configuration. This directive imports a custom error page to use for an error that originates from HAProxy, such as 503 Service Unavailable and 504 Gateway Timeout. It has the ability to render a static HTML file from an http-errors section when given the errorfiles parameter. Here is an example of how to add this new resource to an already existing backend section named test:

$ curl -X POST \
-–user admin:password \
-H "Content-Type: application/json" \
-d '{
"index": 0,
"status": 503,
"errorfiles": "website-1"
}' \
"http://127.0.0.1:5555/v2/services/haproxy/configuration/http_error_rules?parent_type=backend&parent_name=test&version=6"

This will create the following line in your backend section in the HAProxy configuration file:

http-error status 503 errorfiles website-1

This line means that for 503 statuses, HAProxy will return whatever file is defined for status 503 in the website-1 errorfiles section.

Support for all options in defaults, frontend, and backend sections

With release 2.7 of the Data Plane API we are happy to announce that the frontend, backend, and defaults resources now support all options in the respective HAProxy sections. Here’s an extensive list of added options for the sections:

/services/haproxy/configuration/backends:

  • persist_rule

  • http_restrict_req_hdr_names

  • tarpit_timeout

  • server_fin_timeout

  • email_alert

  • load_server_state_from_file

  • server_state_file_name

  • description

  • id

  • enabled

  • disabled

  • use_fcgi_app

  • error_files

  • errorfiles_from_http_errors

  • errorloc302

  • errorloc303

  • fullconn

  • force_persist

  • ignore_persist

  • http_send_name_header

  • max_keep_alive_queue

  • retry_on

  • source

/services/haproxy/configuration/frontends:

  • client_fin_timeout

  • tarpit_timeout

  • http_restrict_req_hdr_names

  • email_alert

  • description

  • id

  • enabled

  • disabled

  • error_files

  • errorfiles_from_http_errors

  • errorloc302

  • errorloc303

  • error_log_format

/services/haproxy/configuration/defaults:

  • hash_type

  • persist_rule

  • http_restrict_req_hdr_names

  • tarpit_timeout

  • email_alert

  • enabled

  • disabled

  • errorfiles_from_http_errors

  • errorloc302

  • errorloc303

  • error_log_format

  • fullconn

  • http_send_name_header

  • max_keep_alive_queue

  • retry_on

  • source

Named Defaults

image3

As of HAProxy 2.4, inheriting settings from a specific defaults section is possible with the use of the from keyword on the line where you define your section and its name, e.g.:

defaults http
mode http
timeout connect 10000
timeout client 30000
timeout server 30000
defaults tcp
mode tcp
frontend fe_1 from http
bind 0.0.0.0:80
frontend fe_2 from tcp
bind 0.0.0.0:3000
timeout connect 20000

This way you can specify that the frontend fe_1 frontend section inherits all the settings specified in the defaults http defaults section, while frontend fe_2 inherits only the settings from the defaults tcp section. This way you don’t have to rely on ordering of your sections to inherit the proper settings. If no from is defined that section will implicitly inherit the settings from the last defaults section specified before that section. So to avoid ambiguity official HAProxy docs suggest naming your defaults settings and specifying from in your sections’ definitions.

To fully support HAProxy configurations, we added support for this in the API, but we still wanted this version to support older versions of HAProxy, namely all versions from HAProxy 2.1 to 2.7 so some compromises in the API were made. Also we didn’t want to break backward compatibility so we created a new resource in the API /services/haproxy/configuration/named_defaults where you can now GET multiple defaults, create new defaults, and delete existing ones to support having multiple defaults sections instead of just one like it was before HAProxy Data Plane API 2.7.

Also defaults resources now have additional name and from fields, while frontend and backend resources now have the from field.

In the HAProxy configuration some settings depend on the ordering, and that creates some implicitness in the configuration. When you are using the HAProxy Data Plane API to configure HAProxy we want to remove as many implicit settings as possible, so in that case, you will see some changes in your HAProxy configurations. No matter which version of HAProxy you are running, you will see that all your defaults sections that don’t have a name will get a name unnamed_defaults_<n>, where n is an automatically incrementing number starting with 1.

If you are running HAProxy 2.4 or newer, it means that HAProxy supports the from keyword, and Data Plane API will enforce usage of that functionality. This means that when first parsing the HAProxy configuration file, Data Plane API will go through all the sections, and if your section does not have the from keyword, it will automatically assign it to the last defaults section it parsed. This way implicit settings become explicit, and it’s easier to parse and debug. To better explain it with an example, this might be a configuration that you already have and want to use the Data Plane API:

defaults
mode http
timeout connect 10000
timeout client 30000
timeout server 30000
frontend fe_1
bind 0.0.0.0:80
defaults
mode tcp
frontend fe_2
bind 0.0.0.0:3000
timeout connect 20000

After first parsing it for the first time, your configuration will be transformed to look like this if you are running HAProxy version 2.4 or newer:

defaults unnamed_defaults_1
mode http
timeout connect 10000
timeout client 30000
timeout server 30000
defaults unnamed_defaults_2
mode tcp
frontend fe_1 from unnamed_defaults_1
bind 0.0.0.0:80
frontend fe_2 from unnamed_defaults_2
bind 0.0.0.0:3000
timeout connect 20000

This way, your configuration is a bit changed, but the original functionality is unchanged and it’s easily representable and changeable via the Data Plane API.

Deprecation warning

To remain backward compatible, the defaults resource will still stay the same: on GET you will always get the last defaults section in the HAProxy configuration, and on PUT you will be changing the same section in the API. This way when you upgrade, you will only have one defaults section and this will be discoverable by both the old defaults resource and the new named_defaults resource. Please note that in the future versions we will be removing the defaults resource so we suggest you migrate to using named_defaults moving forward.

Dynamic Server Configuration

image1

In addition to all the HAProxy configuration changes that version 2.7 brings to the Data Plane API, we have implemented support for add server and del server commands from the HAProxy Runtime API. Those commands were added in HAProxy 2.4 so they won’t work if you try to run them in older versions of HAProxy.

To use the new add and del commands, use the existing /services/haproxy/runtime/servers API resource. We’ve added POST and DELETE methods to support add and del server commands respectively.

Important

When using any runtime/ resources, changes are only applied through the HAProxy Runtime API and are not persisted in the configuration file. This means that those changes are strictly transient and will not survive a reload or a restart.

With the ability to use these commands through the Runtime API, we’ve added some changes to how the /services/haproxy/configuration/servers resource behaves. So if you are running HAProxy 2.4 or newer and you are adding a server, this change will be done automatically through the Runtime API and no reload will be issued. There are a couple of caveats to this though, as mentioned in the official documentation for the Runtime API add server command: the Runtime API supports only a subset of server fields, so if your server has any non-supported fields, the Data Plane API will issue a reload on the HAProxy process. Also given that the server you are adding in through the Runtime API doesn’t take into account any default settings specified in the default-server configuration line, if there is a default-server line set in your backend or the relevant defaults section, the Data Plane API will again issue a reload.

We’ve opted not to do the same for the deletion of servers, as that may hinder your running servers and cause them to forcefully drop connections. In the future we are looking to improve this in the Runtime API, so that the server first drains its connections before it’s deleted, which it doesn’t do now, so upon deletion of /services/haproxy/configuration/servers, the Data Plane API will still issue a reload to avoid any unwanted dropped connections for the mentioned server.

Reload Strategies

One quality of life improvement that the Data Plane API 2.7 brings is something we call reload strategies. In previous versions, to set the HAProxy reload commands you would have configured something like this:

YAML:

haproxy:
config_file: "/etc/haproxy/haproxy.cfg"
haproxy_bin: "haproxy"
reload:
reload_cmd: "systemctl reload haproxy"
restart_cmd: "systemctl restart haproxy"
status_cmd: "systemctl status haproxy"

Now we’ve implemented reload strategies. In the reload section of the Data Plane API configuration, you can set the new reload_strategy option to either systemd, s6, or custom. To remain backward compatible, the upper snippet will still work as a custom reload strategy. If you set any of the other supported values, HAProxy will by default use the following commands:

For systemd:

reload_cmd: "sudo -n systemctl reload haproxy.service"
restart_cmd: "sudo -n systemctl restart haproxy.service"
status_cmd: "systemctl --quiet is-active haproxy.service"

For s6:

reload_cmd: "s6-svc -2 /etc/service.d/haproxy"
restart_cmd: "s6-svc -r /etc/service.d/haproxy"
status_cmd: "s6-svstat -u /etc/service.d/haproxy"

In addition, you can customize the service name using the service_name option. For example, now if you use systemd to manage an HAProxy process called haproxy, you can set this in the configuration:

YAML:

haproxy:
config_file: "/etc/haproxy/haproxy.cfg"
haproxy_bin: "haproxy"
reload:
reload_strategy: "systemd"
service_name: "haproxy-2.4"

The same is true if you are using the s6 init system. Note that service_name is optional, and the default values are as mentioned before.

Another thing that changed in HAProxy 2.7 is improved reload handling in the master Runtime API. So if you are running HAProxy 2.7 in master-worker mode with a master socket configured in the Data Plane API, the Data Plane API will use it for reloads no matter what you configured, as it is the most reliable method. Before HAProxy 2.7 the reload command would just kill the socket connection, leaving you with no information about the outcome of the reload. In 2.7, by contrast, reload behaves synchronously, so when you issue a reload command, you can wait for the response and see whether the reload succeeded or failed, and you get the output of the reload command which enables you to more consistently revert configuration in case the reload failed.

Library Updates & Bug Fixes

In addition to the many enhancements described above, HAProxy Data Plane API 2.7 introduces some stability and infrastructure improvements as well as bug fixes.

One of the bigger updates is that the Data Plane API project has been migrated to Go 1.19 along with the underlying libraries, config-parser, and client-native. This migration allows us to get all the new features of the Go language to improve our codebase, along with some optimizations and, of course, security fixes.

In addition, we performed a thorough scan of all external dependencies and performed updates as needed for bug and security fixes.

There have been 58 commits of bug fixes in the Data Plane API project and its underlying libraries since the v2.6.0 release.

Version Note

With Data Plane API release 2.7 we have caught up the HAProxy versioning numbers, and with this, we’ll be moving from our quarterly release schedule to the same release schedule as HAProxy, meaning a bi-yearly release cadence with one LTS version.

Data Plane API 2.7 supports all HAProxy versions from 2.1 to 2.7. The plan, moving forward with the same versioning numbers, is to keep it one-to-one with HAProxy versions, so that each HAProxy gets its matching version of Data Plane API. This will be implemented and announced in one of the future versions of the Data Plane API.

Read more:

Conclusion and Contributors

With this release, we’re proud to say we’ve achieved our goal of full HAProxy configuration support!

We’d like to thank the code contributors who helped achieve this important milestone:

Contributor

Area

Alexander Duryagin

BUG

Andjelko Iharos

BUG FEATURE

Dario Tranchitella

FEATURE OPTIMIZATION REORG TEST

Dinko Korunic

BUG FEATURE OPTIMIZATION

George Vine

BUG FEATURE

Goran Galinec

BUG CLEANUP FEATURE

Karan Sharma

BUG FEATURE

Marko Juraga

BUG BUILD CLEANUP DOC FEATURE TEST

Olivier Duclos

BUG DOC FEATURE TEST

Robert Maticevic

BUG FEATURE

Tim Brabant

BUG

Zlatko Bratkovic

BUG BUILD CLEANUP FEATURE TEST

To check the latest release, visit our GitHub releases page.

Subscribe to our blog. Get the latest release updates, tutorials, and deep-dives from HAProxy experts.