diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ff7d14e..db9403f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -29,8 +29,6 @@ jobs:
echo ::set-output name=build_date::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
echo ::set-output name=docker_username::librenmsbot
echo ::set-output name=docker_image::librenms/librenms
- echo ::set-output name=quay_username::librenms+travis
- echo ::set-output name=quay_image::quay.io/librenms/librenms
-
name: Docker Build
run: |
@@ -40,30 +38,24 @@ jobs:
--build-arg "VERSION=${{ steps.prepare.outputs.version }}" \
--tag "${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}" \
--tag "${{ steps.prepare.outputs.docker_image }}:latest" \
- --tag "${{ steps.prepare.outputs.quay_image }}:${{ steps.prepare.outputs.version }}" \
- --tag "${{ steps.prepare.outputs.quay_image }}:latest" \
--file Dockerfile .
-
name: Docker Login
if: success() && startsWith(github.ref, 'refs/tags/')
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
- QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
run: |
echo "${DOCKER_PASSWORD}" | docker login --username "${{ steps.prepare.outputs.docker_username }}" --password-stdin
- echo "${QUAY_PASSWORD}" | docker login quay.io --username "${{ steps.prepare.outputs.quay_username }}" --password-stdin
-
name: Docker Push
if: success() && startsWith(github.ref, 'refs/tags/')
run: |
docker push ${{ steps.prepare.outputs.docker_image }}
- docker push ${{ steps.prepare.outputs.quay_image }}
-
name: Docker Check Manifest
if: always() && startsWith(github.ref, 'refs/tags/')
run: |
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}
- docker run --rm mplatform/mquery ${{ steps.prepare.outputs.quay_image }}:${{ steps.prepare.outputs.version }}
-
name: Clear
if: always() && startsWith(github.ref, 'refs/tags/')
diff --git a/README.md b/README.md
index a7a20b1..9393545 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@
-
@@ -20,19 +19,17 @@ It's a fork of [CrazyMax's LibreNMS Docker image repository](https://github.com/
## Features
* Run as non-root user
-* Cron tasks as a ["sidecar" container](doc/notes/crons.md)
-* Syslog-ng support through a ["sidecar" container](doc/notes/syslog-ng.md)
+* [Dispatcher service](doc/docker/environment-variables.md#dispatcher-service-under-test) or legacy [cron jobs](doc/docker/environment-variables.md#cron) as "sidecar" containers
+* Syslog-ng support through a ["sidecar" container](doc/docker/environment-variables.md#syslog-ng)
* Ability to configure [distributed polling](https://docs.librenms.org/Extensions/Distributed-Poller/)
* Ability to add custom Monitoring plugins (Nagios)
* OPCache enabled to store precompiled script bytecode in shared memory
* [s6-overlay](https://github.com/just-containers/s6-overlay/) as process supervisor
* [Traefik](https://github.com/containous/traefik-library-image) as reverse proxy and creation/renewal of Let's Encrypt certificates (see [this template](examples/traefik))
* [Memcached](https://github.com/docker-library/memcached) image ready to use for better scalability
-* [RRDcached](https://github.com/crazy-max/docker-rrdcached) image ready to use for better scalability
+* [RRDcached](https://github.com/crazy-max/docker-rrdcached) image ready to use for data caching and graphs
* [Postfix SMTP relay](https://github.com/juanluisbaptiste/docker-postfix) image to send emails
* [MariaDB](https://github.com/docker-library/mariadb) image as database instance
-* Cron jobs as a ["sidecar" container](doc/docker/environment-variables.md#cron)
-* Syslog-ng support through a ["sidecar" container](doc/docker/environment-variables.md#syslog-ng)
## Documentation
@@ -46,6 +43,7 @@ It's a fork of [CrazyMax's LibreNMS Docker image repository](https://github.com/
* [Add user](doc/notes/add-user.md)
* [Validate](doc/notes/validate.md)
* [Update database](doc/notes/update-database.md)
+ * [Dispatcher service](doc/notes/dispatcher-service.md)
* [Crons](doc/notes/crons.md)
* [Syslog-ng](doc/notes/syslog-ng.md)
* [Additional Monitoring plugins (Nagios)](doc/notes/additional-monitoring-plugins.md)
diff --git a/doc/docker/environment-variables.md b/doc/docker/environment-variables.md
index 5e1408e..d754acf 100644
--- a/doc/docker/environment-variables.md
+++ b/doc/docker/environment-variables.md
@@ -13,19 +13,32 @@
* `REAL_IP_HEADER`: Request header field whose value will be used to replace the client address (default `X-Forwarded-For`)
* `LOG_IP_VAR`: Use another variable to retrieve the remote IP address for access [log_format](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) on Nginx. (default `remote_addr`)
-### (Distributed) Poller
+### Dispatcher service
-* `LIBRENMS_POLLER_THREADS`: Threads that `poller-wrapper.py` runs (default `16`)
-* `LIBRENMS_POLLER_INTERVAL`: Interval in minutes at which `poller-wrapper.py` runs (defaults to `5`) [docs](https://docs.librenms.org/Support/1-Minute-Polling/)
-* `LIBRENMS_DISTRIBUTED_POLLER_ENABLE`: Enable distributed poller functionality
-* `LIBRENMS_DISTRIBUTED_POLLER_NAME`: Optional name of poller (defaults to hostname)
-* `LIBRENMS_DISTRIBUTED_POLLER_GROUP`: By default, all hosts are shared and have the poller_group = 0. To pin a device to a poller, set it to a value greater than 0 and set the same value here. One can also specify a comma separated string of poller groups. The poller will then poll devices from any of the groups listed. [docs](https://docs.librenms.org/#Extensions/Distributed-Poller/#distributed-poller)
-* `LIBRENMS_DISTRIBUTED_POLLER_MEMCACHED_HOST`: Memcached server for poller synchronization (Defaults to `$MEMCACHED_HOST`)
-* `LIBRENMS_DISTRIBUTED_POLLER_MEMCACHED_PORT`: Port of memcached server (Defaults to `$MEMCACHED_PORT`)
+> :warning: Only used if you enable and run a [sidecar dispatcher container](../notes/crons.md)
+
+* `SIDECAR_DISPATCHER`: Set to `1` to enable sidecar dispatcher mode for this container (default `0`)
+* `LIBRENMS_SERVICE_POLLER_WORKERS`: Processes spawned for polling (default `24`)
+* `LIBRENMS_SERVICE_SERVICES_WORKERS`: Processes spawned for service polling (default `8`)
+* `LIBRENMS_SERVICE_DISCOVERY_WORKERS`: Processes spawned for discovery (default `16`)
+* `LIBRENMS_SERVICE_POLLER_FREQUENCY`: Seconds between polling attempts (default `300`)
+* `LIBRENMS_SERVICE_SERVICES_FREQUENCY`: Seconds between service polling attempts (default `300`)
+* `LIBRENMS_SERVICE_DISCOVERY_FREQUENCY`: Seconds between polling attempts (default `21600`)
+* `LIBRENMS_SERVICE_BILLING_FREQUENCY`: Seconds between polling attempts (default `300`)
+* `LIBRENMS_SERVICE_BILLING_CALCULATE_FREQUENCY`: Billing interval (default `60`)
+* `LIBRENMS_SERVICE_POLLER_DOWN_RETRY`: Seconds between failed polling attempts (default `60`)
+* `LIBRENMS_SERVICE_LOGLEVEL`: Must be one of 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL' (default `INFO`)
+* `LIBRENMS_SERVICE_UPDATE_FREQUENCY`: Seconds between LibreNMS update checks (default `86400`)
+* `LIBRENMS_SERVICE_PING_ENABLED`: Enable fast ping scheduler (default `false`)
+* `LIBRENMS_SERVICE_WATCHDOG_ENABLED`: Enable watchdog scheduler (default `false`)
+* `REDIS_HOST`: Redis host for poller synchronization (default `localhost`)
+* `REDIS_PORT`: Redis port (default `6379`)
+* `REDIS_PASSWORD`: Redis password
+* `REDIS_DB`: Redis database (default `0`)
### Cron
-> :warning: Only used if you enabled and run a [sidecar cron container](../notes/crons.md)
+> :warning: Only used if you enable and run a [sidecar cron container](../notes/crons.md)
* `SIDECAR_CRON`: Set to `1` to enable sidecar cron mode for this container (default `0`)
* `LIBRENMS_CRON_DISCOVERY_ENABLE`: Enable LibreNMS discovery for this container cronjobs (default `true`)
@@ -42,9 +55,19 @@
* `LIBRENMS_CRON_SNMPSCAN_THREADS`: SNMP network scanning threads to use (default `32`)
* `LIBRENMS_CRON_SNMPSCAN_LOGFILE`: SNMP network scanning cron log file (default `/dev/null`)
+### Distributed Poller
+
+* `LIBRENMS_POLLER_THREADS`: Threads that `poller-wrapper.py` runs (default `16`)
+* `LIBRENMS_POLLER_INTERVAL`: Interval in minutes at which `poller-wrapper.py` runs (default `5`) [docs](https://docs.librenms.org/Support/1-Minute-Polling/)
+* `LIBRENMS_DISTRIBUTED_POLLER_ENABLE`: Enable distributed poller functionality
+* `LIBRENMS_DISTRIBUTED_POLLER_NAME`: Optional name of poller (default `$(hostname)`)
+* `LIBRENMS_DISTRIBUTED_POLLER_GROUP`: By default, all hosts are shared and have the poller_group = 0. To pin a device to a poller, set it to a value greater than 0 and set the same value here. One can also specify a comma separated string of poller groups. The poller will then poll devices from any of the groups listed. [docs](https://docs.librenms.org/#Extensions/Distributed-Poller/#distributed-poller)
+* `LIBRENMS_DISTRIBUTED_POLLER_MEMCACHED_HOST`: Memcached server for poller synchronization (default `$MEMCACHED_HOST`)
+* `LIBRENMS_DISTRIBUTED_POLLER_MEMCACHED_PORT`: Port of memcached server (default `$MEMCACHED_PORT`)
+
### Syslog-ng
-> :warning: Only used if you enabled and run a [sidecar syslog-ng container](../notes/syslog-ng.md)
+> :warning: Only used if you enable and run a [sidecar syslog-ng container](../notes/syslog-ng.md)
* `SIDECAR_SYSLOGNG`: Set to `1` to enable sidecar syslog-ng mode for this container (default `0`)
diff --git a/doc/docker/ports.md b/doc/docker/ports.md
index 4ee5562..0e7b9f9 100644
--- a/doc/docker/ports.md
+++ b/doc/docker/ports.md
@@ -1,4 +1,4 @@
### Ports
* `8000`: HTTP port
-* `514 514/udp`: Syslog ports (only used if you enabled and run a [sidecar syslog-ng container](../notes/syslog-ng.md))
+* `514 514/udp`: Syslog ports (only used if you enable and run a [sidecar syslog-ng container](../notes/syslog-ng.md))
diff --git a/doc/notes/crons.md b/doc/notes/crons.md
index c89fd48..dcde7cc 100644
--- a/doc/notes/crons.md
+++ b/doc/notes/crons.md
@@ -12,3 +12,5 @@ docker run -d --name librenms_cron \
> `-v librenms:/data`
> :warning: `librenms` must be a valid volume already attached to a LibreNMS container
+
+> :no_entry: Can't be used with [sidecar dispatcher container](crons.md).
diff --git a/doc/notes/dispatcher-service.md b/doc/notes/dispatcher-service.md
new file mode 100644
index 0000000..bc73802
--- /dev/null
+++ b/doc/notes/dispatcher-service.md
@@ -0,0 +1,16 @@
+## Dispatcher service
+
+If you want to enable the new [Dispatcher service](https://docs.librenms.org/Extensions/Dispatcher-Service/), you have to run a "sidecar" container (see dispatcher service in [docker-compose.yml](../../examples/dispatcher/docker-compose.yml) example) or run a simple container like this:
+
+```bash
+docker run -d --name librenms_dispatcher \
+ --env-file $(pwd)/librenms.env \
+ -e SIDECAR_DISPATCHER=1 \
+ -v librenms:/data \
+ librenms/librenms:latest
+```
+
+> `-v librenms:/data`
+> :warning: `librenms` must be a valid volume already attached to a LibreNMS container
+
+> :no_entry: Can't be used with [sidecar cron container](crons.md).
diff --git a/examples/dispatcher/.env b/examples/dispatcher/.env
new file mode 100644
index 0000000..a847768
--- /dev/null
+++ b/examples/dispatcher/.env
@@ -0,0 +1,11 @@
+MYSQL_DATABASE=librenms
+MYSQL_USER=librenms
+MYSQL_PASSWORD=asupersecretpassword
+
+SMTP_SERVER=smtp.example.com
+SMTP_USERNAME=smtp@example.com
+SMTP_PASSWORD=
+
+TZ=Europe/Paris
+PUID=1000
+PGID=1000
diff --git a/examples/dispatcher/docker-compose.yml b/examples/dispatcher/docker-compose.yml
new file mode 100644
index 0000000..47f61c6
--- /dev/null
+++ b/examples/dispatcher/docker-compose.yml
@@ -0,0 +1,149 @@
+version: "3.5"
+
+services:
+ db:
+ image: mariadb:10.2
+ container_name: librenms_db
+ command:
+ - "mysqld"
+ - "--sql-mode="
+ - "--innodb-file-per-table=1"
+ - "--lower-case-table-names=0"
+ - "--character-set-server=utf8"
+ - "--collation-server=utf8_unicode_ci"
+ volumes:
+ - "./db:/var/lib/mysql"
+ environment:
+ - "TZ=${TZ}"
+ - "MYSQL_ALLOW_EMPTY_PASSWORD=yes"
+ - "MYSQL_DATABASE=${MYSQL_DATABASE}"
+ - "MYSQL_USER=${MYSQL_USER}"
+ - "MYSQL_PASSWORD=${MYSQL_PASSWORD}"
+ restart: always
+
+ memcached:
+ image: memcached:alpine
+ container_name: librenms_memcached
+ environment:
+ - "TZ=${TZ}"
+ restart: always
+
+ redis:
+ image: redis:5.0-alpine
+ container_name: librenms_redis
+ environment:
+ - "TZ=${TZ}"
+ restart: always
+
+ rrdcached:
+ image: crazymax/rrdcached
+ container_name: librenms_rrdcached
+ volumes:
+ - "./librenms/rrd:/data/db"
+ - "./rrd-journal:/data/journal"
+ environment:
+ - "TZ=${TZ}"
+ - "PUID=${PUID}"
+ - "PGID=${PGID}"
+ - "LOG_LEVEL=LOG_INFO"
+ - "WRITE_TIMEOUT=1800"
+ - "WRITE_JITTER=1800"
+ - "WRITE_THREADS=4"
+ - "FLUSH_DEAD_DATA_INTERVAL=3600"
+ restart: always
+
+ smtp:
+ image: juanluisbaptiste/postfix
+ container_name: librenms_smtp
+ environment:
+ - "SERVER_HOSTNAME=librenms.example.com"
+ - "SMTP_SERVER=${SMTP_SERVER}"
+ - "SMTP_USERNAME=${SMTP_USERNAME}"
+ - "SMTP_PASSWORD=${SMTP_PASSWORD}"
+ restart: always
+
+ librenms:
+ image: librenms/librenms:latest
+ container_name: librenms
+ domainname: example.com
+ hostname: librenms
+ ports:
+ - target: 8000
+ published: 8000
+ protocol: tcp
+ depends_on:
+ - db
+ - memcached
+ - rrdcached
+ - smtp
+ volumes:
+ - "./librenms:/data"
+ environment:
+ - "TZ=${TZ}"
+ - "PUID=${PUID}"
+ - "PGID=${PGID}"
+ - "DB_HOST=db"
+ - "DB_NAME=${MYSQL_DATABASE}"
+ - "DB_USER=${MYSQL_USER}"
+ - "DB_PASSWORD=${MYSQL_PASSWORD}"
+ - "DB_TIMEOUT=60"
+ env_file:
+ - "./librenms.env"
+ restart: always
+
+ dispatcher:
+ image: librenms/librenms:latest
+ container_name: librenms_dispatcher
+ domainname: example.com
+ hostname: librenms
+ depends_on:
+ - librenms
+ - redis
+ volumes:
+ - "./librenms:/data"
+ environment:
+ - "TZ=${TZ}"
+ - "PUID=${PUID}"
+ - "PGID=${PGID}"
+ - "DB_HOST=db"
+ - "DB_NAME=${MYSQL_DATABASE}"
+ - "DB_USER=${MYSQL_USER}"
+ - "DB_PASSWORD=${MYSQL_PASSWORD}"
+ - "DB_TIMEOUT=60"
+ - "REDIS_HOST=redis"
+ - "REDIS_PORT=6379"
+ - "REDIS_DB=0"
+ - "SIDECAR_DISPATCHER=1"
+ env_file:
+ - "./librenms.env"
+ restart: always
+
+ syslog-ng:
+ image: librenms/librenms:latest
+ container_name: librenms_syslog
+ domainname: example.com
+ hostname: librenms
+ depends_on:
+ - librenms
+ ports:
+ - target: 514
+ published: 514
+ protocol: tcp
+ - target: 514
+ published: 514
+ protocol: udp
+ volumes:
+ - "./librenms:/data"
+ environment:
+ - "TZ=${TZ}"
+ - "PUID=${PUID}"
+ - "PGID=${PGID}"
+ - "DB_HOST=db"
+ - "DB_NAME=${MYSQL_DATABASE}"
+ - "DB_USER=${MYSQL_USER}"
+ - "DB_PASSWORD=${MYSQL_PASSWORD}"
+ - "DB_TIMEOUT=60"
+ - "SIDECAR_SYSLOGNG=1"
+ env_file:
+ - "./librenms.env"
+ restart: always
diff --git a/examples/dispatcher/librenms.env b/examples/dispatcher/librenms.env
new file mode 100644
index 0000000..6b8655e
--- /dev/null
+++ b/examples/dispatcher/librenms.env
@@ -0,0 +1,28 @@
+MEMORY_LIMIT=256M
+UPLOAD_MAX_SIZE=16M
+OPCACHE_MEM_SIZE=128
+REAL_IP_FROM=0.0.0.0/32
+REAL_IP_HEADER=X-Forwarded-For
+LOG_IP_VAR=remote_addr
+
+LIBRENMS_SNMP_COMMUNITY=librenmsdocker
+MEMCACHED_HOST=memcached
+MEMCACHED_PORT=11211
+RRDCACHED_HOST=rrdcached
+RRDCACHED_PORT=42217
+
+LIBRENMS_SERVICE_POLLER_WORKERS=24
+LIBRENMS_SERVICE_SERVICES_WORKERS=8
+LIBRENMS_SERVICE_DISCOVERY_WORKERS=16
+
+LIBRENMS_SERVICE_POLLER_FREQUENCY=300
+LIBRENMS_SERVICE_SERVICES_FREQUENCY=300
+LIBRENMS_SERVICE_DISCOVERY_FREQUENCY=21600
+LIBRENMS_SERVICE_BILLING_FREQUENCY=300
+LIBRENMS_SERVICE_BILLING_CALCULATE_FREQUENCY=60
+LIBRENMS_SERVICE_POLLER_DOWN_RETRY=60
+LIBRENMS_SERVICE_LOGLEVEL=INFO
+LIBRENMS_SERVICE_UPDATE_FREQUENCY=86400
+
+LIBRENMS_SERVICE_PING_ENABLED=false
+LIBRENMS_SERVICE_WATCHDOG_ENABLED=false
diff --git a/rootfs/etc/cont-init.d/03-config.sh b/rootfs/etc/cont-init.d/03-config.sh
index 9647c81..b26bd31 100644
--- a/rootfs/etc/cont-init.d/03-config.sh
+++ b/rootfs/etc/cont-init.d/03-config.sh
@@ -47,9 +47,6 @@ DB_NAME=${DB_NAME:-librenms}
DB_USER=${DB_USER:-librenms}
DB_TIMEOUT=${DB_TIMEOUT:-30}
-SIDECAR_CRON=${SIDECAR_CRON:-0}
-SIDECAR_SYSLOGNG=${SIDECAR_SYSLOGNG:-0}
-
# Timezone
echo "Setting timezone to ${TZ}..."
ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime
diff --git a/rootfs/etc/cont-init.d/04-svc-main.sh b/rootfs/etc/cont-init.d/04-svc-main.sh
index 16d70b5..5dda362 100644
--- a/rootfs/etc/cont-init.d/04-svc-main.sh
+++ b/rootfs/etc/cont-init.d/04-svc-main.sh
@@ -29,9 +29,10 @@ DB_USER=${DB_USER:-librenms}
DB_TIMEOUT=${DB_TIMEOUT:-60}
SIDECAR_CRON=${SIDECAR_CRON:-0}
+SIDECAR_DISPATCHER=${SIDECAR_DISPATCHER:-0}
SIDECAR_SYSLOGNG=${SIDECAR_SYSLOGNG:-0}
-if [ "$SIDECAR_CRON" = "1" ] || [ "$SIDECAR_SYSLOGNG" = "1" ]; then
+if [ "$SIDECAR_CRON" = "1" ] || [ "$SIDECAR_DISPATCHER" = "1" ] || [ "$SIDECAR_SYSLOGNG" = "1" ]; then
exit 0
fi
diff --git a/rootfs/etc/cont-init.d/05-svc-dispatcher.sh b/rootfs/etc/cont-init.d/05-svc-dispatcher.sh
new file mode 100644
index 0000000..ebe8f02
--- /dev/null
+++ b/rootfs/etc/cont-init.d/05-svc-dispatcher.sh
@@ -0,0 +1,112 @@
+#!/usr/bin/with-contenv bash
+
+# From https://github.com/docker-library/mariadb/blob/master/docker-entrypoint.sh#L21-L41
+# usage: file_env VAR [DEFAULT]
+# ie: file_env 'XYZ_DB_PASSWORD' 'example'
+# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
+# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
+file_env() {
+ local var="$1"
+ local fileVar="${var}_FILE"
+ local def="${2:-}"
+ if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
+ echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
+ exit 1
+ fi
+ local val="$def"
+ if [ "${!var:-}" ]; then
+ val="${!var}"
+ elif [ "${!fileVar:-}" ]; then
+ val="$(< "${!fileVar}")"
+ fi
+ export "$var"="$val"
+ unset "$fileVar"
+}
+
+DB_PORT=${DB_PORT:-3306}
+DB_DATABASE=${DB_DATABASE:-librenms}
+DB_USERNAME=${DB_USERNAME:-librenms}
+DB_TIMEOUT=${DB_TIMEOUT:-60}
+
+SIDECAR_DISPATCHER=${SIDECAR_DISPATCHER:-0}
+
+LIBRENMS_SERVICE_POLLER_WORKERS=${LIBRENMS_SERVICE_POLLER_WORKERS:-24}
+LIBRENMS_SERVICE_SERVICES_WORKERS=${LIBRENMS_SERVICE_SERVICES_WORKERS:-8}
+LIBRENMS_SERVICE_DISCOVERY_WORKERS=${LIBRENMS_SERVICE_DISCOVERY_WORKERS:-16}
+
+LIBRENMS_SERVICE_POLLER_FREQUENCY=${LIBRENMS_SERVICE_POLLER_FREQUENCY:-300}
+LIBRENMS_SERVICE_SERVICES_FREQUENCY=${LIBRENMS_SERVICE_SERVICES_FREQUENCY:-300}
+LIBRENMS_SERVICE_DISCOVERY_FREQUENCY=${LIBRENMS_SERVICE_DISCOVERY_FREQUENCY:-21600}
+LIBRENMS_SERVICE_BILLING_FREQUENCY=${LIBRENMS_SERVICE_BILLING_FREQUENCY:-300}
+LIBRENMS_SERVICE_BILLING_CALCULATE_FREQUENCY=${LIBRENMS_SERVICE_BILLING_CALCULATE_FREQUENCY:-60}
+LIBRENMS_SERVICE_POLLER_DOWN_RETRY=${LIBRENMS_SERVICE_POLLER_DOWN_RETRY:-60}
+LIBRENMS_SERVICE_LOGLEVEL=${LIBRENMS_SERVICE_LOGLEVEL:-INFO}
+LIBRENMS_SERVICE_UPDATE_FREQUENCY=${LIBRENMS_SERVICE_UPDATE_FREQUENCY:-86400}
+
+LIBRENMS_SERVICE_PING_ENABLED=${LIBRENMS_SERVICE_PING_ENABLED:-false}
+LIBRENMS_SERVICE_WATCHDOG_ENABLED=${LIBRENMS_SERVICE_WATCHDOG_ENABLED:-false}
+
+REDIS_HOST=${REDIS_HOST:-localhost}
+REDIS_PORT=${REDIS_PORT:-6379}
+file_env 'REDIS_PASSWORD'
+REDIS_DB=${REDIS_DB:-0}
+
+# Continue only if sidecar dispatcher container
+if [ "$SIDECAR_DISPATCHER" != "1" ]; then
+ exit 0
+fi
+
+echo ">>"
+echo ">> Sidecar dispatcher container detected"
+echo ">>"
+
+file_env 'DB_PASSWORD'
+if [ -z "$DB_PASSWORD" ]; then
+ >&2 echo "ERROR: Either DB_PASSWORD or DB_PASSWORD_FILE must be defined"
+ exit 1
+fi
+
+dbcmd="mysql -h ${DB_HOST} -P ${DB_PORT} -u "${DB_USERNAME}" "-p${DB_PASSWORD}""
+unset DB_PASSWORD
+
+echo "Waiting ${DB_TIMEOUT}s for database to be ready..."
+counter=1
+while ! ${dbcmd} -e "show databases;" > /dev/null 2>&1; do
+ sleep 1
+ counter=$((counter + 1))
+ if [ ${counter} -gt ${DB_TIMEOUT} ]; then
+ >&2 echo "ERROR: Failed to connect to database on $DB_HOST"
+ exit 1
+ fi;
+done
+echo "Database ready!"
+counttables=$(echo 'SHOW TABLES' | ${dbcmd} "$DB_DATABASE" | wc -l)
+
+# Configuration
+cat > ${LIBRENMS_PATH}/config.d/dispatcher.php < /etc/services.d/dispatcher/run <