Skip to content

dataplaneapi

Jesús Daniel Colmenares Oviedo edited this page Aug 15, 2025 · 2 revisions

Using HAProxy with Data Plane API

Load-balancing and fault-tolerance in services can be very complex, but fortunately there are projects that make this task a little more pleasant. In this article, we will show you how to use the HAProxy (or technically Data Plane API) integration with Overlord.

dataplaneapi-metadata.yml:

kind: metadata
datacenters:
  main:
    entrypoint: !ENV '${ENTRYPOINT}'
    access_token: !ENV '${TOKEN}'
deployIn:
  labels:
    - desktop
    - centralita
metadata:
  haproxy.makejail: |
    OPTION start
    OPTION overwrite=force
    OPTION volume=dataplaneapi-data mountpoint:/usr/local/etc/haproxy owner:0 group:0

    INCLUDE gh+DtxdF/efficient-makejail

    CMD mkdir -p /usr/local/etc/pkg/repos
    COPY ${OVERLORD_METADATA}/dataplaneapi.pkg.conf /usr/local/etc/pkg/repos/Latest.conf

    PKG haproxy dataplaneapi

    CMD mkdir -p /usr/local/etc/haproxy
    RAW if appjail cmd jexec "${APPJAIL_JAILNAME}" [ ! -f /usr/local/etc/haproxy/haproxy.cfg ]; then
      COPY ${OVERLORD_METADATA}/haproxy.conf /usr/local/etc/haproxy/haproxy.cfg
    RAW fi

    SYSRC haproxy_enable=YES
    SYSRC haproxy_config=/usr/local/etc/haproxy/haproxy.cfg

    SYSRC dataplaneapi_enable=YES
    
    SERVICE haproxy start
    SERVICE dataplaneapi start
  haproxy.conf: !ENV |
    global
      daemon
      stats socket /var/run/haproxy.sock mode 666 level admin
      stats timeout 2m
      log 127.0.0.1:514 local0
      log-tag HAProxy

    defaults
      mode http
      log global
      option httplog
      timeout http-request 10s
      timeout connect 10s
      timeout client 30s
      timeout server 50s

    userlist controller
      user admin insecure-password ${DATAPLANEAPI_PASSWORD}

    frontend stats
      mode http
      bind :8404
      stats uri /
      stats enable
      stats refresh 10s
      stats show-modules
      stats auth admin:${DATAPLANEAPI_STATS_PASSWORD}

    frontend web
      bind :4080
      default_backend web

    backend web
      balance roundrobin
      option httpchk HEAD /
  dataplaneapi.pkg.conf: |
    FreeBSD: {
      url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
      mirror_type: "srv",
      signature_type: "fingerprints",
      fingerprints: "/usr/share/keys/pkg",
      enabled: yes
    }

    FreeBSD-kmods: {
      enabled: no
    }

Now we need to define the deployment file to deploy HAProxy with Data Plane API. Remember that now this service will act as a reverse proxy to the service we want to deploy, so for the client's point of view, instead of using the IP address of the server where the service is deployed, we will use the IP address of the server where HAProxy is deployed. The port is entirely up to you, but in the above case the frontend is configured to use port 4080. The backend is important for Overlord as it is the part that touches to add, update or remove your service from the HAProxy configuration file.

Note that we also copy the HAProxy configuration file only once. First, because we are using a volume to persist the data generated by Data Plane API, and second, because we should not overwrite the HAProxy configuration file since Data Plane API has probably already touched it.

dataplaneapi.yml:

kind: directorProject
datacenters:
  main:
    entrypoint: !ENV '${ENTRYPOINT}'
    access_token: !ENV '${TOKEN}'
deployIn:
  labels:
    - desktop
    - centralita
projectName: load-balancer
projectFile: |
  options:
    - alias:
    - ip4_inherit:
  services:
    haproxy:
      makejail: !ENV '${OVERLORD_METADATA}/haproxy.makejail'
      options:
        - label: 'overlord.skydns:1'
        - label: 'overlord.skydns.group:haproxy'
        - label: 'overlord.skydns.interface:tailscale0'
      volumes:
        - data: dataplaneapi-data
  default_volume_type: '<volumefs>'
  volumes:
    data:
      device: /var/appjail-volumes/dataplaneapi/data

The next step is to configure each API server. In the deployment file above, we will deploy HAProxy and Data Plane API in two chains. The idea is that each instance is located in different places in the country (as in my case). The other thing worth mentioning is SkyDNS, as it is used in the deployment above, so it also balances the load of each HAProxy instance from the client side.

/usr/local/etc/overlord.yml:

dataplaneapi:
  auth:
    username: 'admin'
    password: '<password>'
  entrypoint: 'http://<IP address or Host Name>:5555'

After all this, we can finally define the deployment file for our service. As in other integrations, Overlord intercepts the labels and performs some tasks. In this case the relevant labels are overlord.load-balancer:1 to use Data Plane API, overlord.load-balancer.backend:web indicating the backend, overlord.load-balancer.interface:tailcale0 the interface to get the IP address, overlord.load-balancer.port the external port of the service that should be accessible from HAProxy, and overlord.load-balancer.set.check:"enabled" an optional parameter to enable HAProxy's healthcheckers.

hello-http-metadata.yml:

kind: metadata
datacenters:
  main:
    entrypoint: !ENV '${ENTRYPOINT}'
    access_token: !ENV '${TOKEN}'
deployIn:
  labels:
    - dc-earth
    - dc-air
  exclude:
    - provider
metadata:
  hello-http.makejail: |
    ARG hello_http_port=80

    OPTION start
    OPTION overwrite=force

    INCLUDE gh+DtxdF/efficient-makejail

    CMD mkdir -p /usr/local/etc/pkg/repos
    COPY ${OVERLORD_METADATA}/hello-http.pkg.conf /usr/local/etc/pkg/repos/Latest.conf

    PKG darkhttpd

    CMD echo "Hello from `uuidgen -r`" > /usr/local/www/darkhttpd/index.html

    SYSRC "darkhttpd_flags=--uid darkhttpd --gid darkhttpd --port ${hello_http_port}"
    SYSRC darkhttpd_enable=YES

    SERVICE darkhttpd start
  hello-http.pkg.conf: |
    FreeBSD: {
      url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
      mirror_type: "srv",
      signature_type: "fingerprints",
      fingerprints: "/usr/share/keys/pkg",
      enabled: yes
    }

    FreeBSD-kmods: {
      enabled: no
    }

hello-http.yml:

kind: directorProject
datacenters:
  main:
    entrypoint: !ENV '${ENTRYPOINT}'
    access_token: !ENV '${TOKEN}'
deployIn:
  labels:
    - dc-earth
    - dc-air
  exclude:
    - provider
projectName: hello-http
projectFile: |
  options:
    - alias:
    - ip4_inherit:
  services:
    darkhttpd:
      makejail: !ENV '${OVERLORD_METADATA}/hello-http.makejail'
      options:
        - label: 'appjail.dns.alt-name:hello-http'
        - label: 'overlord.load-balancer:1'
        - label: 'overlord.load-balancer.backend:web'
        - label: 'overlord.load-balancer.interface:tailscale0'
        - label: !ENV 'overlord.load-balancer.interface.port:${OVERLORD_FREEPORT_TAILSCALE0}'
        - label: 'overlord.load-balancer.set.check:"enabled"'
      arguments:
        - hello_http_port: !ENV '${OVERLORD_FREEPORT_TAILSCALE0}'
reserve_port:
  tailscale0:

The last step is to deploy the previous deployment files.

# overlord apply -f dataplaneapi-metadata.yml
# overlord apply -f dataplaneapi.yml
# overlord get-info -f dataplaneapi.yml -t projects --filter-per-project
datacenter: http://controller.namespace.lan:8888
  entrypoint: main
  chain: None
  labels:
    - all
    - desktop
    - services
    - vm-only
    - dc-air
  projects:
    load-balancer:
      state: DONE
      last_log: 2025-08-15_15h58m28s
      locked: False
      services:
        - {'name': 'haproxy', 'status': 0, 'jail': 'b60ffd4e76'}
      up:
        operation: COMPLETED
        output:
         rc: 0
         stdout: {'errlevel': 0, 'message': None, 'failed': []}
        last_update: 3 hours, 11 minutes and 2.83 seconds
        job_id: 31
        restarted: False
        labels:
         error: False
         message: None
         load-balancer:
           services:
             haproxy:
               error: False
               message: None
         skydns:
           services:
             haproxy:
               error: False
               message: (project:load-balancer, service:haproxy, records:[address:True,ptr:None,srv:None] records has been updated.
datacenter: http://controller.namespace.lan:8888
  entrypoint: main
  chain: centralita
  labels:
    - all
    - centralita
    - services
    - dc-earth
  projects:
    load-balancer:
      state: DONE
      last_log: 2025-08-15_15h58m29s
      locked: False
      services:
        - {'name': 'haproxy', 'status': 0, 'jail': 'c05455807f'}
      up:
        operation: COMPLETED
        output:
         rc: 0
         stdout: {'errlevel': 0, 'message': None, 'failed': []}
        last_update: 3 hours, 10 minutes and 24.54 seconds
        job_id: 5
        restarted: False
        labels:
         error: False
         message: None
         load-balancer:
           services:
             haproxy:
               error: False
               message: None
         skydns:
           services:
             haproxy:
               error: False
               message: (project:load-balancer, service:haproxy, records:[address:True,ptr:None,srv:None] records has been updated.
# overlord apply -f hello-http-metadata.yml
# overlord apply -f hello-http.yml
# overlord get-info -f hello-http.yml -t projects --filter-per-project
datacenter: http://controller.namespace.lan:8888
  entrypoint: main
  chain: None
  labels:
    - all
    - desktop
    - services
    - vm-only
    - dc-air
  projects:
    hello-http:
      state: DONE
      last_log: 2025-08-15_18h59m07s
      locked: False
      services:
        - {'name': 'darkhttpd', 'status': 0, 'jail': '8f8f7c848e'}
      up:
        operation: COMPLETED
        output:
         rc: 0
         stdout: {'errlevel': 0, 'message': None, 'failed': []}
        last_update: 9 minutes and 24.42 seconds
        job_id: 33
        restarted: False
        labels:
         error: False
         message: None
         load-balancer:
           services:
             darkhttpd:
               error: False
               message: (project:hello-http, service:darkhttpd, backend:web, serverid:0d67d160-61af-4810-b277-5fb9e20da8eb, code:202, transaction_id:b6b2cd6e-2d0f-4b54-9f30-1e4dfb149637, commit:1) server has been successfully added.
         skydns:
           services:
             darkhttpd:
               error: False
               message: None
datacenter: http://controller.namespace.lan:8888
  entrypoint: main
  chain: provider.carl
  labels:
    - all
    - carl
    - services
    - dc-earth
  projects:
    hello-http:
      state: DONE
      last_log: 2025-08-15_18h59m36s
      locked: False
      services:
        - {'name': 'darkhttpd', 'status': 0, 'jail': '130edf0c32'}
      up:
        operation: COMPLETED
        output:
         rc: 0
         stdout: {'errlevel': 0, 'message': None, 'failed': []}
        last_update: 8 minutes and 48.42 seconds
        job_id: 3
        restarted: False
        labels:
         error: False
         message: None
         load-balancer:
           services:
             darkhttpd:
               error: False
               message: (project:hello-http, service:darkhttpd, backend:web, serverid:02c29ac1-eadc-4a16-835d-6377f4a01aea, code:202, transaction_id:f670f33d-2018-4d4d-8ec0-bb7308ced563, commit:1) server has been successfully added.
         skydns:
           services:
             darkhttpd:
               error: False
               message: None
datacenter: http://controller.namespace.lan:8888
  entrypoint: main
  chain: centralita
  labels:
    - all
    - centralita
    - services
    - dc-earth
  projects:
    hello-http:
      state: DONE
      last_log: 2025-08-15_18h59m08s
      locked: False
      services:
        - {'name': 'darkhttpd', 'status': 0, 'jail': '0ab7ba1640'}
      up:
        operation: COMPLETED
        output:
         rc: 0
         stdout: {'errlevel': 0, 'message': None, 'failed': []}
        last_update: 8 minutes and 41.32 seconds
        job_id: 7
        restarted: False
        labels:
         error: False
         message: None
         load-balancer:
           services:
             darkhttpd:
               error: False
               message: (project:hello-http, service:darkhttpd, backend:web, serverid:f4b9e170-67bb-403e-88da-112c55b45fce, code:202, transaction_id:cf16b162-5584-41e7-9b94-af25b81bb615, commit:1) server has been successfully added.
         skydns:
           services:
             darkhttpd:
               error: False
               message: None
datacenter: http://controller.namespace.lan:8888
  entrypoint: main
  chain: r2
  labels:
    - all
    - r2
    - services
    - dc-air
  projects:
    hello-http:
      state: DONE
      last_log: 2025-08-15_18h59m14s
      locked: False
      services:
        - {'name': 'darkhttpd', 'status': 0, 'jail': 'b4bd3f771b'}
      up:
        operation: COMPLETED
        output:
         rc: 0
         stdout: {'errlevel': 0, 'message': None, 'failed': []}
        last_update: 3 minutes and 42.37 seconds
        job_id: 31
        restarted: False
        labels:
         error: False
         message: None
         load-balancer:
           services:
             darkhttpd:
               error: False
               message: (project:hello-http, service:darkhttpd, backend:web, serverid:73ddf13e-cd81-420d-9c67-835f4cdafd69, code:202, transaction_id:e97527fd-4cc3-44ed-bd9c-ad454faf4293, commit:1) server has been successfully added.
         skydns:
           services:
             darkhttpd:
               error: False
               message: None

Testing:

$ curl http://haproxy.overlord.lan:4080
Hello from 18a7ff90-4773-4970-95ad-9ad2e0012a71
$ curl http://haproxy.overlord.lan:4080
Hello from bd36b060-d80e-401f-b3dd-972e1d4a4435
$ curl http://haproxy.overlord.lan:4080
Hello from 64b3cffb-bf8f-48dd-83d8-fb61cd46f2b9
$ curl http://haproxy.overlord.lan:4080
Hello from 3f5c2c3c-b3f0-4d2d-9c3d-2e40c654d827

Clone this wiki locally