Manually installing Semaphore


Content:


This documentation goes into the details on how to set-up Semaphore when using these installation methods:

The Semaphore software-package is just a part of the whole system needed to successfully run Ansible with it.

The Python3- and Ansible-Execution-Environment are also very important!

NOTE: There are existing Ansible-Galaxy Roles that handle this setup-logic for you or can be used as a base-template for your own Ansible Role!


Service User

Semaphore does not need to be run as user root - so you shouldn't.

Benefits of using a service user:

  • Has its own user-config
  • Has its own environment
  • Processes easily identifiable
  • Gained system security

You can create a system user either manually by using adduser or using the ansible.builtin.user module.

In this documentation we will assume:

  • the service user creates is named semaphore
  • it has the shell /bin/bash set
  • its home directory is /home/semaphore

Troubleshooting

If the Ansible execution of Semaphore is failing - you will need to troubleshoot it in the context of the service user.

You have multiple options to do so:

  • Change your whole shell session to be in the user's context:

    sudo su --login semaphore
    
  • Run a single command in the user's context:

    sudo --login -u semaphore <command>
    

Python3

Ansible is build using the Python3 programming language.

So its clean setup is essential for Ansible to work correctly.

First - make sure the packages python3 and python3-pip are installed on your system!

You have multiple options to install required Python modules:

  • Installing them in the service user's context
  • Installing them in a service-specific Virtual Environment

Requirements

Either way - it is recommended to use a requirements.txt file to specify the modules that need to be installed.

We will assume the file /home/semaphore/requirements.txt is used.

Here is an example of its content:

ansible
# for common jinja-filters
netaddr
jmespath
# for common modules
pywinrm
passlib
requests
docker

NOTE: You should also update those requirements from time to time!

An option for doing this automatically is also shown in the service example below.

Modules in user context

Manually:

sudo --login -u semaphore python3 -m pip install --user --upgrade -r /home/semaphore/requirements.txt

Using Ansible:

- name: Install requirements
  ansible.builtin.pip:
    requirements: '/home/semaphore/requirements.txt'
    extra_args: '--user --upgrade'
  become_user: 'semaphore'

Modules in a virtualenv

We will assume the virtualenv is created at /home/semaphore/venv

Make sure the virtual environment is activated inside the Service! This is also shown in the service example below.

Manually:

sudo su --login semaphore
python3 -m pip install --user virtualenv
python3 -m vitualenv /home/semaphore/venv
# activate the context of the virtual environment
source /home/semaphore/venv/bin/activate
# verify we are using python3 from inside the venv
which python3
> /home/semaphore/venv/bin/python3
python3 -m pip install --upgrade -r /home/semaphore/requirements.txt
# disable the context to the virtual environment
deactivate

Using Ansible:

- name: Create virtual environment and install requirements into it
  ansible.builtin.pip:
    requirements: '/home/semaphore/requirements.txt'
    virtualenv: '/home/semaphore/venv'
    state: present  # or 'latest' to upgrade the requirements

Troubleshooting

If you encounter Python3 issues when using a virtual environment, you will need to change into its context to troubleshoot them:

sudo su --login semaphore
source /home/semaphore/venv/bin/activate
# verify we are using python3 from inside the venv
which python3
> /home/semaphore/venv/bin/python3

# troubleshooting

deactivate

Sometimes a virtual environment also breaks on system upgrades. If this happens you might just remove the existing one and re-create it.


Ansible Collections & Roles

You might want to pre-install Ansible modules and roles, so they don't need to be installed every time a task runs!

Requirements

It is recommended to use a requirements.yml file to specify the modules that need to be installed.

We will assume the file /home/semaphore/requirements.yml is used.

Here is an example of its content:

---

collections:
  - 'namespace.collection'
  # for common collections:
  - 'community.general'
  - 'ansible.posix'
  - 'community.mysql'
  - 'community.crypto'

roles:
  - src: 'namespace.role'

See also: Installing Collections, Installing Roles

NOTE: You should also update those requirements from time to time!

An option for doing this automatically is also shown in the service example below.

Install in user-context

Manually:

sudo su --login semaphore
ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml
ansible-galaxy role install --force -r /home/semaphore/requirements.yml

Install when using a virtualenv

Manually:

sudo su --login semaphore
source /home/semaphore/venv/bin/activate
# verify we are using python3 from inside the venv
which python3
> /home/semaphore/venv/bin/python3

ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml
ansible-galaxy role install --force -r /home/semaphore/requirements.yml

deactivate

Reverse Proxy

See: Security - Encrypted connection


Extended Systemd Service

Here is the basic template of the systemd service.

Add additional settings under their [PART]

Base

[Unit]
Description=Semaphore UI
Documentation=https://docs.semaphoreui.com/
Wants=network-online.target
After=network-online.target
ConditionPathExists=/usr/bin/semaphore
ConditionPathExists=/etc/semaphore/config.json

[Service]
ExecStart=/usr/bin/semaphore server --config /etc/semaphore/config.json
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

Service user

[Service]
User=semaphore
Group=semaphore

Python Modules

In user-context

[Service]
# to auto-upgrade python modules at service startup
ExecStartPre=/bin/bash -c 'python3 -m pip install --upgrade --user -r /home/semaphore/requirements.txt'

# so the executables are found
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/semaphore/.local/bin"
# set the correct python path. You can get the correct path with: python3 -c "import site; print(site.USER_SITE)" 
Environment="PYTHONPATH=/home/semaphore/.local/lib/python3.10/site-packages"

In virtualenv

[Service]
# to auto-upgrade python modules at service startup
ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \
                           && python3 -m pip install --upgrade -r /home/semaphore/requirements.txt'

# REPLACE THE EXISTING 'ExecStart'
ExecStart=/bin/bash -c 'source /home/semaphore/venv/bin/activate \
                        && /usr/bin/semaphore server --config /etc/semaphore/config.json'

Ansible Collections & Roles

If using Python3 in user-context

[Service]
# to auto-upgrade ansible collections and roles at service startup
ExecStartPre=/bin/bash -c 'ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml'
ExecStartPre=/bin/bash -c 'ansible-galaxy role install --force -r /home/semaphore/requirements.yml'

If using Python3 in virtualenv

# to auto-upgrade ansible collections and roles at service startup
ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \
                           && ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml \
                           && ansible-galaxy role install --force -r /home/semaphore/requirements.yml'

Other use-cases

Using local MariaDB

[Unit]
Requires=mariadb.service

Using local Nginx

[Unit]
Wants=nginx.service

Sending logs to syslog

[Service]
StandardOutput=journal
StandardError=journal
SyslogIdentifier=semaphore

Full Examples

Python Modules in user-context

[Unit]
Description=Semaphore UI
Documentation=https://docs.semaphoreui.com/
Wants=network-online.target
After=network-online.target
ConditionPathExists=/usr/bin/semaphore
ConditionPathExists=/etc/semaphore/config.json

[Service]
User=semaphore
Group=semaphore
Restart=always
RestartSec=10s
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:~/.local/bin"

ExecStartPre=/bin/bash -c 'ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml'
ExecStartPre=/bin/bash -c 'ansible-galaxy role install --force -r /home/semaphore/requirements.yml'
ExecStartPre=/bin/bash -c 'python3 -m pip install --upgrade --user -r /home/semaphore/requirements.txt'

ExecStart=/usr/bin/semaphore server --config /etc/semaphore/config.json
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

Python Modules in virtualenv

[Unit]
Description=Semaphore UI
Documentation=https://docs.semaphoreui.com/
Wants=network-online.target
After=network-online.target
ConditionPathExists=/usr/bin/semaphore
ConditionPathExists=/etc/semaphore/config.json

[Service]
User=semaphore
Group=semaphore
Restart=always
RestartSec=10s

ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \
                           && python3 -m pip install --upgrade -r /home/semaphore/requirements.txt'
ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \
                           && ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml \
                           && ansible-galaxy role install --force -r /home/semaphore/requirements.yml'

ExecStart=/bin/bash -c 'source /home/semaphore/venv/bin/activate \
                        && /usr/bin/semaphore server --config /etc/semaphore/config.json'
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

Fixes

If you have a custom system language set - you might run into problems that can be resoled by updating the associated environmental variables:

[Service]
Environment=LANG="en_US.UTF-8"
Environment=LC_ALL="en_US.UTF-8"

Troubleshooting

If there is a problem while executing a task it might be an environmental issue with your setup - not an issue with Semaphore itself!

Please go through these steps to verify if the issue occurs outside Semaphore:

  • Change into the context of the user:

    sudo su --login semaphore
    
  • Change into the context of the virtualenv if you use one:

    source /home/semaphore/venv/bin/activate
    # verify we are using python3 from inside the venv
    which python3
    > /home/semaphore/venv/bin/python3
    
    # troubleshooting
    
    deactivate
    
  • Run the Ansible Playbook manually

    • If it fails => there is an issue with your environment
    • If it works:
      • Re-check your configuration inside Semaphore
      • It might be an issue with Semaphore