Addons
Addons are a way to extend the functionality of the core system. They are currently implemented as a way to add new service components to the system.
Hint
text Whenever [NAME] is mentioned in the article below, it should be replaced with the name of the addon in all lowercase letters.
Addon Structure
Addons could be stored in a single .zip file. However, for more complexity and functionality, they are now packaged and shipped in a .deb file.
A good starting point is to use the addon creator inside the Libre Workspace Portal (Systemkonfiguration -> Addons -> Create Addon). It will generate the basic structure for you, even if you are not using any Docker containers.
The addon control files are stored in the directory /var/lib/libre-workspace/modules/[NAME]
.
It should contain the following structure:
- /var/lib/libre-workspace/modules/[NAME]/
- [NAME].conf
- setup_[NAME].sh
- update_[NAME].sh
- remove_[NAME].sh
- update_env.sh (optional)
- [NAME].png / [NAME].svg / [NAME].jpg / [NAME].webp
- LICENSE
- patches/ (optional)
- 1970-01-01_my_first_patch.sh (optional)
- any other files needed for the installation of the addon (optional)
If you want to distribute your config, add a LICENSE file. Apache 2.0 is highly recommended: https://www.apache.org/licenses/LICENSE-2.0.html#apply )
An example of the structure of the NocoDB addon would be:
- /var/lib/libre-workspace/modules/nocodb/
- patches/ (optional)
- 1970-01-01_my_first_patch.sh (optional)
- nocodb.conf
- setup_nocodb.sh
- update_nocodb.sh
- remove_nocodb.sh
- nocodb.png
- LICENSE
Create the structure for your addon
To get your addon template, you can use the addon creator in the web interface at Systemkonfiguration -> Addons-Center -> Create Addon.
If you are not using Docker, you can simply insert a
#
into the field and fill in the other fields.Unpack the generated .zip file and edit all files to your needs.
[NAME].conf
This file contains the configuration for the addon. It is a simple text file with the following format:
id="[NAME]"
name="[Nice Name]"
description="[DESCRIPTION]"
author="[AUTHOR]"
email="[EMAIL]"
url="[URL]"
An example of nocodb.conf would be:
id="nocodb"
name="NocoDB"
description="Database Management Tool"
author="NocoDB Developers"
email="db@example.com"
url="db"
The URL should be only the third-level domain name, without the protocol or the path.
The shorter, the better. Make sure it doesn’t conflict with any other addon you want to install.
You are able to add multiple URLs to the addon. Just separate them with a comma. For example: url="db,db2"
In special cases, you can also keep the URL empty. Then set the URL to url=""
.
setup_[NAME].sh
This file is a simple shell script which is executed when the administrator installs the service (or module) in the system configuration. It is automatically executed as root. These variables are passed to the script:
$DOMAIN: The domain name of the service, for example:
int.de
$ADMIN_PASSWORD: The password of the administrator which is used for the LDAP instance or the system user “systemv” which also has admin rights with sudo
$IP: The IP address of the server
$LDAP_DC: The domain component of the LDAP instance
$LANGUAGE_CODE: The language code of the system, like “de” or “en”
It is a good practice to store the config of the service in the /root/[NAME]
directory, for example, the docker-compose.yml file.
The addon detection is based on the existence of this folder. For example, even patches are not run if this folder /root/[NAME]
does not exist. And it will be easier for system administrators to find the config of the service in the future.
Also, you have to remember to add an entry to the /etc/caddy/Caddyfile
to make the service accessible.
The current working directory is the root directory of the addon. It may be at /var/lib/libre-workspace/modules/[NAME].
Please do not use cd
in all your scripts, because it could lead to unexpected behavior. At least if you are using it, make sure to run cd -
at the end.
An example of setup_nocodb.sh would be:
#!/bin/bash
# This script gets three variables passed: $DOMAIN, $ADMIN_PASSWORD, $IP, $LDAP_DC, $LANGUAGE_CODE
mkdir -p /root/nocodb
# Don't forget to escape " with a backslash:
echo "
services:
nocodb:
depends_on:
root_db:
condition: service_healthy
environment:
NC_DB: \"mysql2://root_db:3306?u=noco&p=faiTh8ra&d=root_db\"
image: \"nocodb/nocodb:latest\"
ports:
- \"23260:8080\"
restart: unless-stopped
volumes:
- \"./nc_data:/usr/app/data\"
root_db:
environment:
MYSQL_DATABASE: root_db
MYSQL_PASSWORD: faiTh8ra
MYSQL_ROOT_PASSWORD: faiTh8ra
MYSQL_USER: noco
healthcheck:
retries: 10
test:
- CMD
- mysqladmin
- ping
- \"-h\"
- localhost
timeout: 20s
image: \"mysql:8.0.32\"
restart: unless-stopped
volumes:
- \"./db_data:/var/lib/mysql\"
" > /root/nocodb/docker-compose.yml
docker compose -f /root/nocodb/docker-compose.yml up -d
echo "db.$DOMAIN {
#tls internal
reverse_proxy localhost:23260
}
" >> /etc/caddy/Caddyfile
# If domain is "int.de" uncomment the tls internal line for internal https
# IT IS RECOMMENDED THAT YOU DO NOT EDIT THESE 3 LINES unless you know what you're doing.
# (Usually, you don't have to edit them at all)
if [ "$DOMAIN" = "int.de" ]; then
sed -i 's/#tls internal/tls internal/g' /etc/caddy/Caddyfile
fi
systemctl restart caddy
You can get inspiration for more complicated setups here: https://github.com/Jean28518/libre-workspace/tree/main/src/usr/lib/libre-workspace/modules
update_[NAME].sh
This file is a simple shell script which is executed when the administrator updates the service (or module) in the system configuration. It is normally executed as root every day if the admin has enabled the automatic updates of this service. If you don’t want to update the service, just leave the file empty. But it’s important to have the file.
An example of update_nocodb.sh would be:
#!/bin/bash
docker compose -f /root/nocodb/docker-compose.yml pull
docker compose -f /root/nocodb/docker-compose.yml up -d
remove_[NAME].sh
This file is a simple shell script which is executed when the administrator removes the service (or module) from the system configuration.
It is automatically executed as root. It is a good practice to remove the complete folder /root/[NAME]
directory, because the addon detection is based on the existence of this folder.
It is also good practice to remove all corresponding data.
For example, if you have a database, you should remove the database and the database user.
The current working directory is the root directory of the addon. It may be at /var/lib/libre-workspace/modules/[NAME].
Please do not use cd
in all your scripts, because it could lead to unexpected behavior. At least if you are using it, make sure to run cd -
at the end.
An example of remove_nocodb.sh would be:
#!/bin/bash
# This script gets three variables passed: $DOMAIN, $ADMIN_PASSWORD, $IP, $LDAP_DC
docker compose -f /root/nocodb/docker-compose.yml down --volumes
rm -rf /root/nocodb
# Remove the entry from the Caddyfile
libre-workspace-remove-webserver-entry db.$DOMAIN
systemctl restart caddy
update_env.sh
This file is a simple shell script which is executed when the administrator updates the environment of the system configuration, which could be the master password (which also changes the LDAP administrator password) or the IP address of the server under which it is accessible. If your addon doesn’t rely on the IP address or the master password, you can ignore this file. It is then not necessary to have it.
In our example of NocoDB, we don’t need this file, because we don’t rely on the IP address or the master password. So we don’t even have to create this file.
patches
For future updates of the addon, you can add patches to the patches folder (which is optional). It is a good practice to name the patch with the date when it was created, so you can easily see the order of the patches. These patch scripts are executed after a daily backup and update of the system or daily at 02:00 am. The run order is based on the filename. The patch with the oldest date is executed first. The patch scripts are executed as root and the current working directory is the root directory of the addon. The environment variables $DOMAIN, $ADMIN_PASSWORD, $IP, $LDAP_DC, and $LANGUAGE_CODE are passed to the script and are available in the script.
The patch should only patch your addon once. But for this, you have to check by yourself if the patched settings are present or not. This can be different for every single patch. Also, it is highly recommended to keep your addon consistent over time, so it should disable itself after 1 year of its release.
Here you can see an example of a Redis patch for Nextcloud:
#!/bin/bash
# IS THIS PATCH OLDER THAN 365 DAYS?
# Get the current file name
FILE_NAME=$(basename $0)
# Get the date of the filename which is like this: 2024-06-25
DATE=${FILE_NAME:0:10}
# Check if the file is older than 365 days
if [ $(( ($(date +%s) - $(date -d $DATE +%s)) / 86400 )) -gt 365 ]; then
echo "Patch is older than 365 days. Exiting patch."
exit 0
fi
# Check if we need to apply the patch
# Is redis installed?
if [ -x "$(command -v redis-server)" ]; then
echo "Redis is already installed. Exiting patch."
exit 0
fi
# BEGIN APPLYING PATCH
# Install redis and php packages
apt-get install redis php-redis php-apcu php-memcache pwgen -y
# ... do the rest
Libre Workspace Commands
To make the addon development easier and more consistent, libre-workspace comes with some helper commands you can/should use in your scripts.
libre-workspace-add-api-key
This command adds an API key to the libre workspace portal. It is useful if your addon needs to access the libre workspace portal API.
Usage: libre-workspace-add-api-key <name> <permissions> <expire_date>
Example: API_KEY=$(libre-workspace-add-api-key 'My API Key' 'linux_client,administrator' '2025-12-31')
If you want to set no expiration date, use '0' as value
libre-workspace-add-oidc-client
This command adds an OIDC client to the libre workspace portal. It is useful if your addon needs to access the libre workspace portal via OIDC.
Usage: libre-workspace-add-oidc-client <name> <client_id> <client_secret> <redirect_uri>
Example: libre-workspace-add-oidc-client 'MyOIDCClient' 'my-client-id' 'my-client-secret' 'https://my-addon.$DOMAIN/callback'
libre-workspace-remove-oidc-client
With this command, you can remove an OIDC client from the libre workspace portal.
Usage: /usr/bin/libre-workspace-remove-oidc-client <name>
Example: libre-workspace-remove-oidc-client 'MyOIDCClient'
libre-workspace-generate-secret
Like pwgen, this command generates a random secret. It is useful if your addon needs a random password or secret.
Usage: libre-workspace-generate-secret [length]
Example: MY_PASSWORD=$(libre-workspace-generate-secret 32)
If no length is given, the default length is 32
libre-workspace-remove-webserver-entry
With this command, you can remove a webserver entry from the Caddy webserver configuration.
Usage: libre-workspace-remove-webserver-entry <url>
Example: libre-workspace-remove-webserver-entry my-addon.$DOMAIN
libre-workspace-send-mail
With this command, you can send an email via the libre workspace portal’s email configuration to the Administrator if he has set it up.
Usage: libre-workspace-send-mail <subject> <message> [attachment_path (optional)]
Example: libre-workspace-send-mail "My Addon" "My Addon was installed successfully with this password: $MY_PASSWORD"
Example: libre-workspace-send-mail "My Addon" "My Addon was installed successfully" "/path/to/attachment.txt"
General Tips
Never experiment on production systems. Always test your scripts on a test system first.
It is a good practice to run the commands line by line manually on a test system to see if everything works as expected.
Please don’t use the
$ADMIN_PASSWORD
variable anymore. Instead, you can get a password withlibre-workspace-generate-secret
.