Documentation

FossID Workbench Ansible Installer Guide

Table of Contents

Automated installation of FossID Workbench using Ansible. The installer supports both local (localhost) and remote (SSH) deployments on Debian/Ubuntu and RHEL-based distributions.

Supported Operating Systems

Family Distributions
Debian Debian 11, 12, 13; Ubuntu 22.04, 24.04, 26.04
RedHat RHEL 8, 9, 10; AlmaLinux 8, 9, 10; RockyLinux 8, 9, 10

Prerequisites

  • Root or sudo access on the target machine
  • Internet connectivity (for downloading packages and the FossID Workbench release)
  • RHEL-based systems: SELinux must be set to permissive mode before running the installer
  • Vault portal credentials configured in config.yml for automatic package download (see Package download from vault portal below), or the FossID Workbench .deb or .rpm package file placed in this directory.

Quick Start

1. Provide the Workbench package

Option A — Vault portal: Have the installer download the latest release from the FossID vault portal. In config.yml, leave fossid_workbench_version unset (or comment it out) and set fossid_vault_base_url, fossid_vault_username, and fossid_vault_password. Use https://vault-eu.foss.id/delivery/download (EU, default) or https://vault-apac.foss.id/delivery/download (APAC) for fossid_vault_base_url. See Package download from vault portal for details.

Option B — Manual: Alternatively, copy the FossID Workbench .deb (Debian/Ubuntu) or .rpm (RHEL) package into this directory alongside install.sh. The package must be named fossid-release_regular_amd64-<version>.deb (Debian/Ubuntu) or fossid-release_regular_x86_64-<version>.rpm (RHEL), where <version> is the actual release version — for example, fossid-release_regular_amd64-25.2.1.deb.

2. Run the bootstrap installer

chmod +x install.sh
./install.sh --local

On the first run, install.sh creates any missing config files from their examples: config.yml from config.yml.example, config.debian.yml from config.debian.yml.example, and config.rhel.yml from config.rhel.yml.example. If any of these were just created, the script exits so you can review and edit the relevant files (see Configuration); then run the same command again to proceed with the installation.

3. Access the application

Once installation completes, open your browser at http://<server-ip> (port 80 by default).

Directory Structure

setup/ansible/
├── install.sh                    # Bootstrap script (entry point)
├── playbook.yml                  # Ansible playbook
├── config.yml.example            # Shared configuration template
├── config.debian.yml.example     # Debian/Ubuntu-specific template
├── config.rhel.yml.example       # RHEL-specific template
├── ansible.cfg                   # Ansible settings
├── inventory.localhost.example   # Inventory for local deployments
├── inventory.remote.example      # Inventory for remote deployments
└── roles/
    └── fossid.fossid_workbench/  # FossID Workbench Ansible role
        ├── requirements.yml      # Galaxy dependencies
        └── ...

install.sh

install.sh is a bootstrap script that handles the full lifecycle: it installs Ansible itself (via pip), pulls Galaxy dependencies, prepares the inventory and config files, and then runs the playbook. You do not need Ansible pre-installed.

Usage

./install.sh [OPTIONS]

Options

Flag Description
-l, --local Run a local deployment (installs on the current machine)
-r, --remote Run a remote deployment (requires an inventory file with SSH targets)
-i, --inventory FILE Use a custom inventory file instead of the defaults
-c, --cleanup Remove Ansible and Galaxy roles installed by the script
-d, --dry-run Print what would be done without making any changes
--skip-update-check Skip the check for pending OS package updates
-v / -vv / -vvv Increase Ansible verbosity (passed through to ansible-playbook)
-h, --help Show help message

Examples

# Local installation
sudo ./install.sh --local

# Dry run (preview without changes)
sudo ./install.sh --local --dry-run

# Remote installation with custom inventory
sudo ./install.sh --remote -i ./my_inventory

# Maximum verbosity for debugging
sudo ./install.sh --local -vvv

# Remove Ansible after installation
sudo ./install.sh --cleanup

What the script does

  1. Detects the package manager (apt, dnf, or yum).
  2. Checks privileges (root or sudo).
  3. Configures locale to avoid encoding errors on minimal installations.
  4. Checks for pending OS updates and refuses to continue if the system is not up to date (skip with --skip-update-check).
  5. Installs Ansible via pip (ansible-core >= 2.15; falls back to 2.11 if Python < 3.9).
  6. Installs Galaxy requirements (roles and collections from roles/fossid.fossid_workbench/requirements.yml).
  7. Prepares the inventory file — uses an existing inventory file, the appropriate example template, or a custom file specified with -i.
  8. Prepares config files — if any of config.yml, config.debian.yml, or config.rhel.yml is missing, copies it from the matching .example file; if any were created, exits so you can review and edit before re-running.
  9. Runs the Ansible playbook (playbook.yml).

playbook.yml

The playbook runs in two plays: first MySQL/MariaDB on database hosts (when not using external DB), then nginx, PHP, and FossID Workbench on application hosts.

Deployment modes

Set mysql_deployment_mode in config.yml to control where MySQL runs:

Mode Description Inventory
local MySQL on the same host(s) as the app (default). Define both workbench_app and workbench_db with the same host(s).
remote MySQL on dedicated host(s). workbench_app = app server(s), workbench_db = DB server(s). Set fossid_conf.webapp_db_server to the DB host, fossid_conf.webapp_db_user_host to '%' or app host, and mysql_bind_address: '0.0.0.0' on DB hosts.
external No MySQL install (e.g. AWS RDS). Only workbench_app; no workbench_db needed. Set fossid_conf.webapp_db_server to the DB endpoint.

Inventory must define the workbench_app group (application hosts). For local or remote mode, also define workbench_db (same as app for local, dedicated hosts for remote). See inventory.localhost.example and inventory.remote.example.

Roles executed (in order)

Play 1 – Database hosts (skipped when mysql_deployment_mode: external):

Role Purpose
geerlingguy.mysql MariaDB/MySQL server

Play 2 – Application hosts:

Role Purpose Condition
geerlingguy.repo-epel EPEL repository RedHat only
geerlingguy.repo-remi Remi PHP repository RedHat only
geerlingguy.nginx Nginx web server Always
geerlingguy.php-versions PHP version management Always
geerlingguy.php PHP runtime and extensions Always
fossid.fossid_workbench FossID Workbench application Always

Pre-tasks

  • Play 1: MariaDB module stream reset and enable on RHEL 8 (on DB hosts).
  • Play 2: Include OS vars, Python 3.11 on RHEL 8 when needed, nginx listen/SSL configuration, www-data user/group on RedHat.

Running the playbook directly

If you already have Ansible installed and Galaxy dependencies in place, you can run the playbook without install.sh:

# Local
ansible-playbook -i inventory.localhost.example playbook.yml --become

# Remote
ansible-playbook -i inventory playbook.yml

# Check mode (dry run)
ansible-playbook -i inventory playbook.yml --check --diff

Configuration

config.yml

All installation parameters are defined in config.yml (created from config.yml.example). Key sections:

Configuration files: config.yml holds shared settings (Workbench version, MySQL mode, nginx, PHP version, etc.). config.debian.yml and config.rhel.yml hold OS-specific overrides (e.g. PHP package lists, RHEL php_enablerepo / EPEL); they are created from config.debian.yml.example and config.rhel.yml.example by install.sh when missing. The playbook always loads config.yml; on application hosts (Play 2), it also includes the Debian or RHEL file based on os_family, so keys in the OS-specific file override config.yml where duplicated.

FossID Workbench

fossid_workbench_version: 25.2.1
admin_user_password: ""
skip_ort_installation: no

fossid_conf:
  cli_server_host: XXXX          # FossID CLI server hostname
  cli_token: ""
  webapp_db_server: localhost     # Database host
  webapp_db_database: fossid      # Database name
  webapp_db_username: fossiduser  # Database user
  webapp_db_password: ""
  webapp_base_url: 'http://localhost/index.php'         # HTTP default; use 'https://<hostname>/index.php' when le_hostname is set
  # webapp_base_url: 'https://workbench.example.com/index.php'  # example for HTTPS with Let's Encrypt

Package download from vault portal

Instead of placing the .deb or .rpm file manually, you can have the installer download the latest FossID release from the FossID vault portal. The download is triggered automatically when fossid_workbench_version is not set (comment it out or remove it from config.yml).

Add or uncomment the following in config.yml:

# fossid_workbench_version: 25.2.1   # comment out or remove to use vault download
fossid_vault_base_url: "https://vault-eu.foss.id/delivery/download"
fossid_vault_username: "your-vault-username"
fossid_vault_password: "your-vault-password"
  • fossid_vault_base_url — Default is the EU endpoint; use https://vault-apac.foss.id/delivery/download for the APAC region (no trailing slash).
  • fossid_vault_username and fossid_vault_password — Both are required. If the password contains special characters (e.g. !), quote it in YAML: fossid_vault_password: "pass!word".
  • To keep credentials out of plain text, store them in an encrypted vault file and reference them via vault_fossid_vault_username / vault_fossid_vault_password (see Securing Secrets with Ansible Vault).

Several values in config.yml reference vault_* variables with a | default('CHANGE_ME') fallback. In production you should store the real secrets in an encrypted vault file (see Securing Secrets with Ansible Vault below) rather than hard-coding them in config.yml.

config.yml variable Vault variable it references
mysql_root_password vault_mysql_root_password
admin_user_password vault_admin_user_password
fossid_conf.cli_token vault_cli_token
fossid_conf.webapp_db_password vault_webapp_db_password
fossid_vault_username vault_fossid_vault_username
fossid_vault_password vault_fossid_vault_password

MySQL/MariaDB and deployment mode

mysql_deployment_mode: local   # or remote, external (see Deployment modes above)
mysql_root_password: ""
# mysql_version: "10.6"
# For remote/external: mysql_admin_login_host, mysql_admin_login_user, mysql_admin_login_password
# For remote: mysql_bind_address: '0.0.0.0'
# For remote/external: fossid_conf.webapp_db_user_host (e.g. '%' or app server hostname)

To add extra MySQL/MariaDB configuration without replacing the role-managed global my.cnf, the geerlingguy.mysql role provides mysql_config_include_files. Each list entry uses src to point at a local file (or Jinja2 template) on the Ansible controller; the role copies it into mysql_config_include_dir on the database host and restarts MySQL/MariaDB when the rendered file changes. Set force: true on an entry to overwrite the destination on later runs if the source file changes; when omitted, the role defaults to not forcing overwrites of existing include files.

mysql_config_include_files:
  - src: "/files/mysql/fossid-extra.cnf"
    force: true

Example fossid-extra.cnf content (create the path on the controller relative to your playbook_dir):

[mysqld]
max_allowed_packet = 256M
innodb_buffer_pool_size = 1G
default_time_zone = 'Europe/Stockholm'   # or e.g. +00:00 — to match the app/OS/PHP

PHP

php_version: "8.4"
# php_enablerepo: "epel,remi-php84"   # Uncomment for RHEL

Nginx

nginx_remove_default_vhost: true
nginx_client_max_body_size: "50m"
nginx_php_fpm_socket: "127.0.0.1:9000"

Securing Secrets with Ansible Vault

config.yml references sensitive values through vault_* variables. These should be defined in an encrypted vault file so that passwords and tokens are never stored in plain text.

1. Create the vault file

ansible-vault create vault.yml

You will be prompted to set a vault password. The editor opens — add the secret variables:

---
vault_mysql_root_password: "your-secure-mysql-root-password"
vault_admin_user_password: "your-secure-admin-password"
vault_cli_token: "your-cli-authentication-token"
vault_webapp_db_password: "your-secure-db-password"

Save and close. The file is now AES-256 encrypted on disk.

2. Edit an existing vault file

ansible-vault edit vault.yml

3. View the encrypted contents without editing

ansible-vault view vault.yml

4. Re-key (change the vault password)

ansible-vault rekey vault.yml

5. Run the playbook with the vault

When running the playbook, Ansible needs the vault password to decrypt vault.yml. There are three ways to provide it:

Interactive prompt (simplest):

sudo ./install.sh --local           # install.sh does not pass --ask-vault-pass yet,
                                     # so run the playbook directly:
ansible-playbook -i inventory playbook.yml --become --ask-vault-pass

Password file (for automation / CI):

Create a file containing only the vault password (e.g. ~/.vault_pass), restrict its permissions, and point Ansible to it:

echo 'my-vault-password' > ~/.vault_pass
chmod 600 ~/.vault_pass

ansible-playbook -i inventory playbook.yml --become --vault-password-file ~/.vault_pass

Or set it permanently in ansible.cfg:

[defaults]
vault_password_file = ~/.vault_pass

Environment variable:

export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass
ansible-playbook -i inventory playbook.yml --become

Without a vault (quick testing only)

If vault.yml does not exist, the playbook will fail because it is listed in vars_files. For quick local testing you can either:

  • Create a minimal unencrypted vault.yml with test values:
---
vault_mysql_root_password: "testpassword"
vault_admin_user_password: "testpassword"
vault_cli_token: "testtoken"
vault_webapp_db_password: "testpassword"
  • Or remove the vault.yml entry from vars_files in playbook.yml — the | default('CHANGE_ME') fallbacks in config.yml will be used instead.

Important: Never commit vault.yml (encrypted or not) or vault password files to version control. Add them to .gitignore.

Inventory Files

Inventory must define workbench_app (and workbench_db for local or remote MySQL). See config.yml.example and the Deployment modes section.

Local deployment

Use or copy inventory.localhost.example:

[workbench_app]
localhost ansible_connection=local

[workbench_db]
localhost

Remote deployment

Copy inventory.remote.example to inventory and edit it. For local mode on a single remote host, put the same host in both groups. For remote mode (dedicated DB server), use different hosts for workbench_app and workbench_db. For external mode (e.g. RDS), define only workbench_app.

For remote deployments, SSH key-based authentication is recommended. If password authentication is required, consider using ansible-vault to encrypt credentials.

Enabling HTTPS (Let’s Encrypt)

To enable automatic SSL certificate provisioning via Let’s Encrypt, add the following to config.yml:

le_hostname: workbench.example.com

When le_hostname is set, the playbook automatically:

  • Switches Nginx to listen on port 443 with SSL
  • Generates an SSL configuration block with modern cipher settings
  • Expects Let’s Encrypt certificates at /etc/letsencrypt/live/<hostname>/

Troubleshooting

System has pending updates

The installer requires the OS to be fully updated before proceeding. Update your system first:

# Debian/Ubuntu
sudo apt update && sudo apt upgrade -y

# RHEL/Fedora
sudo dnf update -y

Or skip the check (not recommended for production):

sudo ./install.sh --local --skip-update-check

SELinux blocking the installation (RHEL)

Set SELinux to permissive mode:

sudo setenforce 0
sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config

Ansible version too old

The script installs Ansible via pip to ensure a recent version. If you see version errors, run cleanup and try again:

sudo ./install.sh --cleanup
sudo ./install.sh --local

Database connection errors

Verify the credentials in config.yml match your fossid_conf settings and that MySQL/MariaDB is running:

sudo systemctl status mysql    # or mariadb

Debugging with verbosity

Increase Ansible output for detailed task logs:

sudo ./install.sh --local -vvv

Cleanup

To remove Ansible and Galaxy roles installed by the script:

sudo ./install.sh --cleanup

This uninstalls ansible-core from pip and removes downloaded Galaxy roles from ~/.ansible/roles.