Table of Contents
- Supported Operating Systems
- Prerequisites
- Quick Start
- Directory Structure
- install.sh
- playbook.yml
- Configuration
- Troubleshooting
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.ymlfor automatic package download (see Package download from vault portal below), or the FossID Workbench.debor.rpmpackage 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
- Detects the package manager (apt, dnf, or yum).
- Checks privileges (root or sudo).
- Configures locale to avoid encoding errors on minimal installations.
- Checks for pending OS updates and refuses to continue if the system is not up to date (skip with
--skip-update-check). - Installs Ansible via pip (ansible-core >= 2.15; falls back to 2.11 if Python < 3.9).
- Installs Galaxy requirements (roles and collections from
roles/fossid.fossid_workbench/requirements.yml). - Prepares the inventory file — uses an existing
inventoryfile, the appropriate example template, or a custom file specified with-i. - Prepares config files — if any of
config.yml,config.debian.yml, orconfig.rhel.ymlis missing, copies it from the matching.examplefile; if any were created, exits so you can review and edit before re-running. - 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/downloadfor 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.ymlwith 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.ymlentry fromvars_filesinplaybook.yml— the| default('CHANGE_ME')fallbacks inconfig.ymlwill 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.