├── .gitignore ├── .vscode └── settings.json ├── README.md ├── book.toml ├── build.sh ├── deploy ├── add_breadcrumbs.py ├── build.yml ├── convert_to_index.sh ├── deploy.yml ├── invs │ └── yc │ │ ├── group_vars │ │ └── all │ │ └── hosts ├── mermaid-preprocessing │ ├── index.js │ ├── package-lock.json │ └── package.json ├── prepare_header_title.sh ├── requirements.yml └── roles │ ├── build │ └── tasks │ │ └── main.yml │ └── deploy │ └── tasks │ └── main.yml ├── mermaid-init.js ├── mermaid.min.js ├── run.sh ├── src ├── .gitbook │ └── assets │ │ ├── 134777345-8789d9e4-ff0d-439c-b80e-ddc56b74fcee.webp │ │ ├── ansible_1.png │ │ ├── ansible_2.png │ │ ├── banner-for-github.jpg │ │ ├── bash_1.png │ │ ├── bash_2.png │ │ ├── bitbucket_access_token_1.webp │ │ ├── bitbucket_access_token_2.webp │ │ ├── bitbucket_access_token_3.png │ │ ├── bitbucket_access_token_3.webp │ │ ├── bitbucket_access_token_4.webp │ │ ├── default-workspace.webp │ │ ├── image (1).png │ │ ├── image (2).png │ │ ├── image (3).png │ │ ├── image (4).png │ │ ├── image (5).png │ │ ├── image (6).png │ │ ├── image (7).png │ │ ├── image (8).png │ │ ├── image.png │ │ ├── integrations_1.jpg │ │ ├── project_new_ipad.png │ │ ├── repository.webp │ │ ├── rpi_3000_project_1_history(iPhone 6_7_8 Plus) (1).png │ │ ├── rpi_3000_project_1_history(iPhone 6_7_8 Plus).png │ │ ├── schedule01.png │ │ ├── schedule02.png │ │ ├── schedule03.png │ │ ├── semaphore-docs-dark.png │ │ ├── semaphore-docs-light.png │ │ ├── task_deploy (1).png │ │ ├── task_deploy.png │ │ ├── template_new (1).png │ │ ├── template_new.png │ │ ├── template_new_build_ipad (1).png │ │ ├── template_new_build_ipad.png │ │ ├── template_new_deploy_ipad.png │ │ ├── template_new_ipad.png │ │ ├── template_schedule.png │ │ ├── template_schedule_commit.png │ │ ├── terraform_1.webp │ │ ├── terraform_2.webp │ │ └── tf-workspace.webp ├── README.md ├── SUMMARY.md ├── administration-guide │ ├── api.md │ ├── cicd.md │ ├── cli.md │ ├── cli │ │ ├── runners.md │ │ ├── users.md │ │ └── vaults.md │ ├── configuration.md │ ├── configuration │ │ ├── cli.md │ │ ├── config-file.md │ │ ├── env-vars.md │ │ └── snap.md │ ├── installation.md │ ├── installation │ │ ├── binary-file.md │ │ ├── cloud.md │ │ ├── docker.md │ │ ├── k8s.md │ │ ├── package-manager.md │ │ └── snap.md │ ├── installation_manually.md │ ├── ldap.md │ ├── logs.md │ ├── notifications.md │ ├── openid.md │ ├── openid │ │ ├── authelia.md │ │ ├── authentik.md │ │ ├── azure.md │ │ ├── gitea.md │ │ ├── github.md │ │ ├── gitlab.md │ │ ├── google.md │ │ ├── keycloak.md │ │ └── okta.md │ ├── runners.md │ ├── security.md │ ├── security │ │ ├── apache.md │ │ ├── database.md │ │ ├── kerberos.md │ │ ├── network.md │ │ └── nginx.md │ ├── troubleshooting.md │ └── upgrading.md ├── faq │ └── troubleshooting.md └── user-guide │ ├── admin │ ├── README.md │ ├── runners.md │ ├── subscription.md │ ├── tasks.md │ └── users.md │ ├── environment.md │ ├── integrations.md │ ├── inventory.md │ ├── inventory │ └── kerberos.md │ ├── key-store.md │ ├── key-store │ └── gitlab.md │ ├── netbox-dynamic-inventory.md │ ├── projects.md │ ├── projects │ ├── activity.md │ ├── history.md │ ├── runners.md │ └── settings.md │ ├── repositories.md │ ├── repositories │ └── bitbucket_access_token.md │ ├── schedules.md │ ├── task-templates │ ├── README.md │ ├── apps │ │ ├── ansible.md │ │ ├── bash.md │ │ ├── powershell.md │ │ ├── python.md │ │ ├── terraform.md │ │ └── terraform │ │ │ ├── states.md │ │ │ └── workspaces.md │ └── survey-vars.md │ ├── tasks.md │ └── team.md └── theme ├── content.css ├── favicon.png ├── head.hbs ├── header.css ├── search.css ├── tabs.css └── tabs.js /.gitignore: -------------------------------------------------------------------------------- 1 | /book 2 | /deploy/invs/yc/files/secrets.yml 3 | /deploy/venv 4 | /book_ok 5 | node_modules/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.root": "/book/", 3 | "filewatcher.commands": [ 4 | ], 5 | "liveServer.settings.port": 5500, 6 | "ansible.python.interpreterPath": "/usr/bin/python3" 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Semaphore Docs 2 | 3 | We use [mdBook](https://github.com/rust-lang/mdBook) engine. -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Denis Gukov"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Semaphore Docs" 7 | 8 | [preprocessor.tabs] 9 | 10 | [preprocessor.mermaid] 11 | command = "mdbook-mermaid" 12 | 13 | # [preprocessor.breadcrumbs] 14 | # command = "/home/fiftin/src/semaphore/mdbook-breadcrumbs/target/release/mdbook-breadcrumbs" 15 | 16 | [output.html] 17 | additional-css = ["theme/tabs.css", "theme/search.css", "theme/content.css", "theme/header.css"] 18 | additional-js = ["theme/tabs.js"] # "mermaid.min.js", "mermaid-init.js" 19 | google-analytics = "G-SXDFVHLVBM" 20 | edit-url-template = "https://github.com/semaphoreui/semaphore-docs/edit/main/{path}" 21 | site-url = "https://semaphoreui.com" 22 | 23 | [output.html.search] 24 | enable = true 25 | limit-results = 10 26 | 27 | [output.html.fold] 28 | enable = true 29 | level = 1 -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | cd deploy 2 | 3 | source ./venv/bin/activate 4 | 5 | ansible-playbook build.yml -------------------------------------------------------------------------------- /deploy/add_breadcrumbs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | def generate_breadcrumbs(summary_file): 5 | breadcrumbs = {} 6 | with open(summary_file, "r") as f: 7 | parent = None 8 | for line in f: 9 | match = re.match(r"\s*\* \[(.+)\]\((.*)\)", line) 10 | if not match: 11 | continue 12 | 13 | print(line) 14 | 15 | name, path = match.groups() 16 | 17 | if path == '': 18 | continue 19 | 20 | if parent: 21 | breadcrumbs[path] = breadcrumbs[parent] + [name] 22 | else: 23 | breadcrumbs[path] = [name] 24 | 25 | return breadcrumbs 26 | 27 | def inject_breadcrumbs(breadcrumbs, content_dir): 28 | print(content_dir) 29 | 30 | for path, breadcrumb in breadcrumbs.items(): 31 | file_path = os.path.join(content_dir, path) 32 | 33 | print(file_path) 34 | 35 | if not os.path.exists(file_path): 36 | continue 37 | 38 | with open(file_path, "r+") as f: 39 | content = f.read() 40 | breadcrumb_html = " > ".join( 41 | f'{n}' for n, p in zip(breadcrumb, breadcrumb[:-1]) 42 | ) 43 | f.seek(0) 44 | f.write(f"\n" + content) 45 | 46 | summary_file = "../src/SUMMARY.md" 47 | content_dir = "../src" 48 | breadcrumbs = generate_breadcrumbs(summary_file) 49 | inject_breadcrumbs(breadcrumbs, content_dir) -------------------------------------------------------------------------------- /deploy/build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Build the book 3 | hosts: localhost 4 | roles: 5 | - build 6 | -------------------------------------------------------------------------------- /deploy/convert_to_index.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to move HTML file to its own directory and rename it to index.html 4 | move_html_to_index() { 5 | local filepath=$1 6 | local dirpath=${filepath%.html} 7 | 8 | # Create directory and move the HTML file 9 | mkdir -p "$dirpath" 10 | mv "$filepath" "$dirpath/index.html" 11 | } 12 | 13 | echo "Go to book directory" 14 | 15 | cd ../book 16 | 17 | echo "Move HTML files to their own directories and rename them to index.html" 18 | 19 | # Find all .html files in the current directory (excluding index.html files) 20 | find . -type f -name "*.html" ! -name "index.html" | while read file; do 21 | move_html_to_index "$file" 22 | done 23 | 24 | 25 | BUILD_VERSION=$RANDOM 26 | 27 | find . -type f -name "index.html" | while read file; do 28 | if [[ "$OSTYPE" == "darwin"* ]]; then 29 | sed -i '' 's/\/index\.html/\//g' "$file" 30 | sed -i '' 's/index\.html/\//g' "$file" 31 | sed -i '' 's/\.html/\//g' "$file" 32 | sed -i '' 's/"\.\.\//"..\/..\//g' "$file" 33 | sed -i '' 's/"\.\//"..\//g' "$file" 34 | 35 | sed -i '' "s/\.css\"/.css\"?v=$BUILD_VERSION\"/g" "$file" 36 | sed -i '' "s/\.js\"/.js\"?v=$BUILD_VERSION\"/g" "$file" 37 | else 38 | sed -i 's/\/index\.html/\//g' "$file" 39 | sed -i 's/index\.html/\//g' "$file" 40 | sed -i 's/\.html/\//g' "$file" 41 | sed -i 's/"\.\.\//"..\/..\//g' "$file" 42 | sed -i 's/"\.\//"..\//g' "$file" 43 | sed -i "s/\.css\"/.css?v=$BUILD_VERSION\"/g" "$file" 44 | sed -i "s/\.js\"/.js?v=$BUILD_VERSION\"/g" "$file" 45 | fi 46 | 47 | if grep -q 'mermaid' "$file"; then 48 | node ../deploy/mermaid-preprocessing/index.js "$file" 49 | fi 50 | done 51 | 52 | 53 | if [[ "$OSTYPE" == "darwin"* ]]; then 54 | sed -i '' "s/\.css/.css?v=$BUILD_VERSION/g" "book.js" 55 | else 56 | sed -i "s/\.css/.css?v=$BUILD_VERSION/g" "book.js" 57 | fi -------------------------------------------------------------------------------- /deploy/deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Deploy the book 3 | hosts: site 4 | roles: 5 | - deploy 6 | -------------------------------------------------------------------------------- /deploy/invs/yc/group_vars/all: -------------------------------------------------------------------------------- 1 | aws_region: ru-central1 2 | website_bucket: docs.semaphoreui.com -------------------------------------------------------------------------------- /deploy/invs/yc/hosts: -------------------------------------------------------------------------------- 1 | [site] 2 | localhost ansible_connection=local ansible_python_interpreter=python3 -------------------------------------------------------------------------------- /deploy/mermaid-preprocessing/index.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import * as cheerio from 'cheerio'; 3 | import { run } from '@mermaid-js/mermaid-cli'; 4 | import path from 'path'; 5 | 6 | async function renderMermaidWithCli(mermaidCode, index) { 7 | const inputFile = `diagram-${index}.mmd`; 8 | const outputFile = `diagram-${index}.svg`; 9 | 10 | // Write Mermaid code to a temporary file 11 | fs.writeFileSync(inputFile, mermaidCode); 12 | 13 | // Use Mermaid CLI's `run` to render the diagram 14 | await run(inputFile, outputFile); 15 | 16 | // Read the rendered SVG content 17 | const svgContent = fs.readFileSync(outputFile, 'utf8'); 18 | 19 | // Clean up temporary files 20 | fs.unlinkSync(inputFile); 21 | fs.unlinkSync(outputFile); 22 | 23 | return svgContent; 24 | } 25 | 26 | async function processHtml(inputFile, outputFile) { 27 | const html = fs.readFileSync(inputFile, 'utf8'); 28 | const $ = cheerio.load(html); 29 | const mermaidBlocks = $('div.mermaid, pre.mermaid'); 30 | 31 | for (let i = 0; i < mermaidBlocks.length; i++) { 32 | const element = mermaidBlocks[i]; 33 | const mermaidCode = $(element).text(); 34 | 35 | try { 36 | console.log(`Rendering diagram ${i} with mermaid-cli...`); 37 | const svg = await renderMermaidWithCli(mermaidCode, i); 38 | $(element).replaceWith(svg); 39 | } catch (error) { 40 | console.error(`Error rendering diagram ${i}:`, error.message); 41 | } 42 | } 43 | 44 | fs.writeFileSync(outputFile, $.html()); 45 | console.log(`Processed HTML written to ${outputFile}`); 46 | } 47 | 48 | // Get CLI arguments 49 | const args = process.argv.slice(2); 50 | if (args.length === 0) { 51 | console.error('Usage: node script.js [outputFile]'); 52 | process.exit(1); 53 | } 54 | 55 | // Process the HTML 56 | processHtml(args[0], args[0]); 57 | -------------------------------------------------------------------------------- /deploy/mermaid-preprocessing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mermaid-preprocessing", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "type": "module", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "description": "", 12 | "dependencies": { 13 | "@mermaid-js/mermaid-cli": "^11.4.2", 14 | "cheerio": "^1.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /deploy/prepare_header_title.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Go to book directory" 3 | 4 | cd ../book 5 | 6 | echo "Move HTML files to their own directories and rename them to index.html" 7 | 8 | find . -type f -name "index.html" | while read file; do 9 | if [[ "$OSTYPE" == "darwin"* ]]; then 10 | sed -i '' 's/>Semaphore Docs//g' "$file" 11 | else 12 | sed -i 's/>Semaphore Docs/>/g' "$file" 13 | fi 14 | done 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /deploy/requirements.yml: -------------------------------------------------------------------------------- 1 | collections: 2 | - name: community.general 3 | - name: community.aws 4 | -------------------------------------------------------------------------------- /deploy/roles/build/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install mdbook 2 | ansible.builtin.command: 3 | cmd: cargo install mdbook --version 0.4.40 4 | creates: "{{ ansible_env.HOME }}/.cargo/bin/mdbook" 5 | 6 | 7 | - name: Install mdbook-tabs 8 | ansible.builtin.command: 9 | cmd: cargo install mdbook-tabs 10 | creates: "{{ ansible_env.HOME }}/.cargo/bin/mdbook-tabs" 11 | 12 | 13 | - name: Remove old book 14 | ansible.builtin.file: 15 | path: "{{ playbook_dir }}/../book" 16 | state: absent 17 | 18 | 19 | - name: Build The Book 20 | ansible.builtin.shell: 21 | # cmd: . {{ ansible_env.HOME }}/.cargo/env && mdbook build 22 | cmd: mdbook build 23 | chdir: "{{ playbook_dir }}/.." 24 | creates: "{{ playbook_dir }}/../book/index.html" 25 | 26 | 27 | - name: Convert all xyz.html to xyz/index.html 28 | ansible.builtin.command: 29 | cmd: "{{ playbook_dir }}/convert_to_index.sh" 30 | register: convert_to_index 31 | changed_when: convert_to_index.stdout != "" 32 | 33 | 34 | - name: Prepare header 35 | ansible.builtin.command: 36 | cmd: "{{ playbook_dir }}/prepare_header_title.sh" 37 | register: prepare_header 38 | changed_when: prepare_header.stdout != "" 39 | -------------------------------------------------------------------------------- /deploy/roles/deploy/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Include secret vars 2 | ansible.builtin.include_vars: 3 | file: "{{ inventory_dir }}/files/secrets.yml" 4 | name: secrets 5 | 6 | 7 | - name: Upload assets to s3 bucket 8 | community.aws.s3_sync: 9 | endpoint_url: https://storage.yandexcloud.net 10 | bucket: "{{ website_bucket }}" 11 | file_change_strategy: force 12 | file_root: "{{ playbook_dir }}/../book/" 13 | aws_access_key: "{{ secrets.aws_access_key }}" 14 | aws_secret_key: "{{ secrets.aws_access_secret }}" 15 | region: "{{ aws_region }}" 16 | cache_control: "public, max-age=31536000" 17 | exclude: "*.html" 18 | 19 | 20 | - name: Upload html to s3 bucket 21 | community.aws.s3_sync: 22 | endpoint_url: https://storage.yandexcloud.net 23 | bucket: "{{ website_bucket }}" 24 | file_change_strategy: force 25 | file_root: "{{ playbook_dir }}/../book/" 26 | aws_access_key: "{{ secrets.aws_access_key }}" 27 | aws_secret_key: "{{ secrets.aws_access_secret }}" 28 | region: "{{ aws_region }}" 29 | cache_control: "public, max-age=600" 30 | mime_map: 31 | ".html": "text/html; charset=UTF-8" 32 | include: "*.html" 33 | -------------------------------------------------------------------------------- /mermaid-init.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const darkThemes = ['ayu', 'navy', 'coal']; 3 | const lightThemes = ['light', 'rust']; 4 | 5 | const classList = document.getElementsByTagName('html')[0].classList; 6 | 7 | let lastThemeWasLight = true; 8 | for (const cssClass of classList) { 9 | if (darkThemes.includes(cssClass)) { 10 | lastThemeWasLight = false; 11 | break; 12 | } 13 | } 14 | 15 | const theme = lastThemeWasLight ? 'default' : 'dark'; 16 | mermaid.initialize({ startOnLoad: true, theme }); 17 | 18 | // Simplest way to make mermaid re-render the diagrams in the new theme is via refreshing the page 19 | 20 | for (const darkTheme of darkThemes) { 21 | document.getElementById(darkTheme).addEventListener('click', () => { 22 | if (lastThemeWasLight) { 23 | window.location.reload(); 24 | } 25 | }); 26 | } 27 | 28 | for (const lightTheme of lightThemes) { 29 | document.getElementById(lightTheme).addEventListener('click', () => { 30 | if (!lastThemeWasLight) { 31 | window.location.reload(); 32 | } 33 | }); 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | cd deploy 2 | 3 | # source ./venv/bin/activate 4 | 5 | ansible-playbook build.yml 6 | 7 | ansible-playbook -i invs/yc/hosts deploy.yml --vault-password-file ~/semaphore_pass -------------------------------------------------------------------------------- /src/.gitbook/assets/134777345-8789d9e4-ff0d-439c-b80e-ddc56b74fcee.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/134777345-8789d9e4-ff0d-439c-b80e-ddc56b74fcee.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/ansible_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/ansible_1.png -------------------------------------------------------------------------------- /src/.gitbook/assets/ansible_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/ansible_2.png -------------------------------------------------------------------------------- /src/.gitbook/assets/banner-for-github.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/banner-for-github.jpg -------------------------------------------------------------------------------- /src/.gitbook/assets/bash_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/bash_1.png -------------------------------------------------------------------------------- /src/.gitbook/assets/bash_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/bash_2.png -------------------------------------------------------------------------------- /src/.gitbook/assets/bitbucket_access_token_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/bitbucket_access_token_1.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/bitbucket_access_token_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/bitbucket_access_token_2.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/bitbucket_access_token_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/bitbucket_access_token_3.png -------------------------------------------------------------------------------- /src/.gitbook/assets/bitbucket_access_token_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/bitbucket_access_token_3.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/bitbucket_access_token_4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/bitbucket_access_token_4.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/default-workspace.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/default-workspace.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (1).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (2).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (3).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (4).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (5).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (6).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (7).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image (8).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image (8).png -------------------------------------------------------------------------------- /src/.gitbook/assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/image.png -------------------------------------------------------------------------------- /src/.gitbook/assets/integrations_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/integrations_1.jpg -------------------------------------------------------------------------------- /src/.gitbook/assets/project_new_ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/project_new_ipad.png -------------------------------------------------------------------------------- /src/.gitbook/assets/repository.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/repository.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/rpi_3000_project_1_history(iPhone 6_7_8 Plus) (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/rpi_3000_project_1_history(iPhone 6_7_8 Plus) (1).png -------------------------------------------------------------------------------- /src/.gitbook/assets/rpi_3000_project_1_history(iPhone 6_7_8 Plus).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/rpi_3000_project_1_history(iPhone 6_7_8 Plus).png -------------------------------------------------------------------------------- /src/.gitbook/assets/schedule01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/schedule01.png -------------------------------------------------------------------------------- /src/.gitbook/assets/schedule02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/schedule02.png -------------------------------------------------------------------------------- /src/.gitbook/assets/schedule03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/schedule03.png -------------------------------------------------------------------------------- /src/.gitbook/assets/semaphore-docs-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/semaphore-docs-dark.png -------------------------------------------------------------------------------- /src/.gitbook/assets/semaphore-docs-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/semaphore-docs-light.png -------------------------------------------------------------------------------- /src/.gitbook/assets/task_deploy (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/task_deploy (1).png -------------------------------------------------------------------------------- /src/.gitbook/assets/task_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/task_deploy.png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_new (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_new (1).png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_new.png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_new_build_ipad (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_new_build_ipad (1).png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_new_build_ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_new_build_ipad.png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_new_deploy_ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_new_deploy_ipad.png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_new_ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_new_ipad.png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_schedule.png -------------------------------------------------------------------------------- /src/.gitbook/assets/template_schedule_commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/template_schedule_commit.png -------------------------------------------------------------------------------- /src/.gitbook/assets/terraform_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/terraform_1.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/terraform_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/terraform_2.webp -------------------------------------------------------------------------------- /src/.gitbook/assets/tf-workspace.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/.gitbook/assets/tf-workspace.webp -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Semaphore UI 2 | 3 | ## What is Semaphore UI? 4 | 5 | Semaphore UI is a modern UI and powerful API for Ansible, Terraform, OpenTofu, PowerShell and other DevOps tools. 6 | 7 | 8 | 9 | Semaphore is written in pure Go and available for Windows, macOS and Linux (x64, ARM, ARM64). Semaphore is an open-source project with concise and high-quality code. 10 | 11 | Semaphore supports the following databases: 12 | 13 | * MySQL 14 | * PostgreSQL 15 | * [BoltDB](https://github.com/etcd-io/bbolt) – embedded key/value database 16 | 17 | With Semaphore you can: 18 | 19 | * [Build, deploy and rollback](./administration-guide/cicd.md) 20 | * Group playbooks to projects 21 | * Manage environments, inventories, repositories and access keys 22 | * Run playbooks from the browser. Responsive UI allows the use of Semaphore on mobile devices 23 | * Run playbooks by schedule 24 | * View detailed logs of any playbook runs, at any time 25 | * Delegate other users the running of playbooks 26 | * Get notifications about playbook runs 27 | 28 | ## Links 29 | 30 | * Source code: [https://github.com/semaphoreui/semaphore](https://github.com/semaphoreui/semaphore) 31 | * Issue tracking: [https://github.com/semaphoreui/semaphore/issues](https://github.com/semaphoreui/semaphore/issues) 32 | * Docker: [https://hub.docker.com/r/semaphoreui/semaphore](https://hub.docker.com/r/semaphoreui/semaphore) 33 | * Snap: [https://snapcraft.io/semaphore](https://snapcraft.io/semaphore) 34 | * Contact: [denis@semaphoreui.com](mailto:denis@semaphoreui.com) 35 | * Docker container configurator: 36 | 37 | [![](https://img.shields.io/badge/docker_configurator-0050ab?style=for-the-badge&logo=docker)](https://semaphoreui.com/install/docker/) 38 | 39 | * Our responsive community: 40 | 41 | [![discord](https://img.shields.io/badge/discord_community-510b80?style=for-the-badge&logo=discord)](https://discord.gg/5R6k7hNGcH) -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Welcome to Semaphore UI](./README.md) 4 | 5 | * [Admin Guide]() 6 | * [Installation](./administration-guide/installation.md) 7 | * [Package manager](./administration-guide/installation/package-manager.md) 8 | * [Docker](./administration-guide/installation/docker.md) 9 | * [Binary file](./administration-guide/installation/binary-file.md) 10 | * [Kubernetes (Helm chart)](./administration-guide/installation/k8s.md) 11 | * [Snap (deprecated)](./administration-guide/installation/snap.md) 12 | * [Manual Installation](./administration-guide/installation\_manually.md) 13 | * [Configuration](./administration-guide/configuration.md) 14 | * [Configuration file](./administration-guide/configuration/config-file.md) 15 | * [Envrioment variables](./administration-guide/configuration/env-vars.md) 16 | * [Interactive setup](./administration-guide/configuration/cli.md) 17 | * [Snap configuration](./administration-guide/configuration/snap.md) 18 | * [Upgrading](./administration-guide/upgrading.md) 19 | * [Security](./administration-guide/security.md) 20 | * [Database security](./administration-guide/security/database.md) 21 | * [Network security](./administration-guide/security/network.md) 22 | * [NGINX config](./administration-guide/security/nginx.md) 23 | * [Apache config](./administration-guide/security/apache.md) 24 | * [CLI](./administration-guide/cli.md) 25 | * [Users](./administration-guide/cli/users.md) 26 | * [Vaults](./administration-guide/cli/vaults.md) 27 | * [Runners](./administration-guide/cli/runners.md) 28 | * [LDAP](./administration-guide/ldap.md) 29 | * [OpenID](./administration-guide/openid.md) 30 | * [GitHub config](./administration-guide/openid/github.md) 31 | * [Google config](./administration-guide/openid/google.md) 32 | * [GitLab config](./administration-guide/openid/gitlab.md) 33 | * [Authelia config](./administration-guide/openid/authelia.md) 34 | * [Authentik config](./administration-guide/openid/authentik.md) 35 | * [Keycloak config](./administration-guide/openid/keycloak.md) 36 | * [Okta config](./administration-guide/openid/okta.md) 37 | * [Azure config](./administration-guide/openid/azure.md) 38 | * [API](./administration-guide/api.md) 39 | * [Pipelines](./administration-guide/cicd.md) 40 | * [Runners](./administration-guide/runners.md) 41 | * [Logs](./administration-guide/logs.md) 42 | * [Notifications](./administration-guide/notifications.md) 43 | 44 | * [User Guide]() 45 | * [Projects](./user-guide/projects.md) 46 | * [History](./user-guide/projects/history.md) 47 | * [Activity](./user-guide/projects/activity.md) 48 | * [Settings](./user-guide/projects/settings.md) 49 | * [Runners (Pro)](./user-guide/projects/runners.md) 50 | * [Task Templates](./user-guide/task-templates/README.md) 51 | * [Ansible](./user-guide/task-templates/apps/ansible.md) 52 | * [Terraform/OpenTofu](./user-guide/task-templates/apps/terraform.md) 53 | * [Workspaces](./user-guide/task-templates/apps/terraform/workspaces.md) 54 | * [HTTP Backend (Pro)](./user-guide/task-templates/apps/terraform/states.md) 55 | * [Shell/Bash scripts](./user-guide/task-templates/apps/bash.md) 56 | * [PowerShell](./user-guide/task-templates/apps/powershell.md) 57 | * [Python](./user-guide/task-templates/apps/python.md) 58 | * [Tasks](./user-guide/tasks.md) 59 | * [Schedules](./user-guide/schedules.md) 60 | * [Key Store](./user-guide/key-store.md) 61 | * [Inventory](./user-guide/inventory.md) 62 | * [Kerberos](./user-guide/inventory/kerberos.md) 63 | * [Variable Groups](./user-guide/environment.md) 64 | * [Repositories](./user-guide/repositories.md) 65 | * [Bitbucket Access Token](./user-guide/repositories/bitbucket_access_token.md) 66 | * [Integrations](./user-guide/integrations.md) 67 | * [Team](./user-guide/team.md) 68 | 73 | 74 | * [FAQ]() 75 | * [Troubleshooting](./faq/troubleshooting.md) 76 | -------------------------------------------------------------------------------- /src/administration-guide/api.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | ## API reference 4 | 5 | Semaphore UI provides two formats of API documentation, so you can choose the one that fits your workflow best: 6 | 7 | * [Swagger/OpenAPI](https://semaphoreui.com/api-docs) — ideal if you prefer an interactive, browser-based experience. 8 | * [Postman](https://api.semaphoreui.com) — perfect if you want to leverage the full power of the Postman app for testing and exploring the API. 9 | 10 | Both options include complete documentation of available endpoints, parameters, and example responses. 11 | 12 | ## Getting Started with the API 13 | 14 | To start using the Semaphore API, you need to generate an API token. 15 | This token must be included in the request header as: 16 | 17 | ```http 18 | Authorization: Bearer YOUR_API_TOKEN 19 | ``` 20 | 21 | ### Creating an API Token 22 | 23 | There are two ways to create an API token: 24 | - Through the web interface (since 2.14) 25 | - Using HTTP request 26 | 27 | #### Through the web interface (since 2.14) 28 | 29 | You can create and manage your API tokens via the Semaphore web UI: 30 | 31 | 32 | 33 | #### Using HTTP request 34 | 35 | You can also authenticate and generate a session token using a direct HTTP request. 36 | 37 | Login to Semaphore (password should be escaped, `slashy\\pass` instead of `slashy\pass` e.g.): 38 | 39 | ```bash 40 | curl -v -c /tmp/semaphore-cookie -XPOST \ 41 | -H 'Content-Type: application/json' \ 42 | -H 'Accept: application/json' \ 43 | -d '{"auth": "YOUR_LOGIN", "password": "YOUR_PASSWORD"}' \ 44 | http://localhost:3000/api/auth/login 45 | ``` 46 | 47 | Generate a new token, and get the new token: 48 | 49 | ```bash 50 | curl -v -b /tmp/semaphore-cookie -XPOST \ 51 | -H 'Content-Type: application/json' \ 52 | -H 'Accept: application/json' \ 53 | http://localhost:3000/api/user/tokens 54 | ``` 55 | 56 | The command should return something similar to: 57 | 58 | ```json 59 | { 60 | "id": "YOUR_ACCESS_TOKEN", 61 | "created": "2025-05-21T02:35:12Z", 62 | "expired": false, 63 | "user_id": 3 64 | } 65 | ``` 66 | --- 67 | 68 | ## Using token to make API requests 69 | 70 | Once you have your API token, include it in the **Authorization** header to authenticate your requests. 71 | 72 | ### Launch a task 73 | 74 | Use this token for launching a task or anything else: 75 | 76 | ```bash 77 | curl -v -XPOST \ 78 | -H 'Content-Type: application/json' \ 79 | -H 'Accept: application/json' \ 80 | -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ 81 | -d '{"template_id": 1}' \ 82 | http://localhost:3000/api/project/1/tasks 83 | ``` 84 | 85 | --- 86 | 87 | ## Expiring an API token 88 | 89 | If you no longer need the token, you should expire it to keep your account secure. 90 | 91 | To manually revoke (expire) an API token, send a DELETE request to the token endpoint: 92 | 93 | ```bash 94 | curl -v -XDELETE \ 95 | -H 'Content-Type: application/json' \ 96 | -H 'Accept: application/json' \ 97 | -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ 98 | http://localhost:3000/api/user/tokens/YOUR_ACCESS_TOKEN 99 | ``` 100 | -------------------------------------------------------------------------------- /src/administration-guide/cicd.md: -------------------------------------------------------------------------------- 1 | # Pipelines 2 | 3 | Semaphore supports simple pipelines with using `build` and `deploy` tasks. 4 | 5 | Semaphore passes `semaphore_vars` variable to each Ansible playbook which it runs. 6 | 7 | You can use it in your Ansible tasks to get what type of task was run, which version should be build or deployed, who ran the task, etc. 8 | 9 | --- 10 | 11 | Example of `semaphore_vars` for `build` tasks: 12 | 13 | ```yaml 14 | semaphore_vars: 15 | task_details: 16 | type: build 17 | username: user123 18 | message: New version of some feature 19 | target_version: 1.5.33 20 | ``` 21 | 22 | Example of `semaphore_vars` for `deploy` tasks: 23 | 24 | ```yaml 25 | semaphore_vars: 26 | task_details: 27 | type: deploy 28 | username: user123 29 | message: Deploy new feature to servers 30 | incoming_version: 1.5.33 31 | ``` 32 | 33 | ### Build 34 | 35 | This type of task is used to create [artifacts](https://en.wikipedia.org/wiki/Artifact\_\(software\_development\)). Each build task has autogenerated version. You should use variable `semaphore_vars.task_details.target_version` in your Ansible playbook to get what version of the artifact should be created. After the artifact is created, it can be used for deployment. 36 | 37 | --- 38 | 39 | Example of `build` Ansible role: 40 | 41 | 1. Get app source code from GitHub 42 | 2. Compile source code 43 | 3. Pack created binary to a tarball with name `app-{{semaphore_vars.task_details.target_version}}.tar.gz` 44 | 4. Send `app-{{semaphore_vars.task_details.target_version}}.tar.gz` to an S3 bucket 45 | 46 | 47 | 48 | ### Deploy 49 | 50 | This type of task is used to deploy artifacts to destination servers. Each deployment task is associated with the build task. You should use variable `semaphore_vars.task_details.incoming_version` in your Ansible playbook to get what version of the artifact should be deployed. 51 | 52 | --- 53 | 54 | Example of `deploy` Ansible role: 55 | 56 | 1. Download `app-{{semaphore_vars.task_details.incoming_version}}.tar.gz` from an S3 bucket to destination servers 57 | 2. Unpack `app-{{semaphore_vars.task_details.incoming_version}}.tar.gz` to destination directory 58 | 3. Create or update configuration files 59 | 4. Restart app service 60 | 61 | -------------------------------------------------------------------------------- /src/administration-guide/cli.md: -------------------------------------------------------------------------------- 1 | # CLI 2 | 3 | 4 | * [Runners](./cli/runners.md) 5 | * [Users](./cli/users.md) 6 | * [Vaults](./cli/vaults.md) 7 | 8 | 9 | ## Common config options 10 | 11 | | Option | Description | 12 | |----------------------|-------------------------------------------| 13 | |`--config config.json`| Path to the configuration file. | 14 | |`--no-config` | Do not use any configuration file. Only environment variable will be used. | 15 | |`--log-level ERROR` | `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`, `PANIC` | 16 | 17 | ## Version 18 | 19 | Print current version. 20 | 21 | ```bash 22 | semaphore version 23 | ``` 24 | 25 | 26 | ## Help 27 | 28 | Print list of supported commands. 29 | 30 | ```bash 31 | semaphore help 32 | ``` 33 | 34 | ## Database migration 35 | 36 | Update database schema to latest version. 37 | 38 | ``` 39 | semaphore migrate 40 | ``` 41 | 42 | ## Interactive setup 43 | 44 | Use this option for first time configuration. 45 | 46 | ``` 47 | semaphore setup 48 | ``` 49 | 50 | ## Server mode 51 | 52 | Start the server. 53 | 54 | ``` 55 | semaphore server 56 | ``` 57 | 58 | ## Runner mode 59 | 60 | Start the runner. 61 | 62 | ``` 63 | semaphore runner 64 | ``` 65 | -------------------------------------------------------------------------------- /src/administration-guide/cli/runners.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Runners 7 | 8 |
9 | We have separate section for Runners. 10 |
-------------------------------------------------------------------------------- /src/administration-guide/cli/users.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Users 7 | 8 | Using CLI you can add, remove or change user. 9 | 10 | ``` 11 | semaphore user --help 12 | ``` 13 | 14 | ## How to add admin user 15 | 16 | ``` 17 | semaphore user add \ 18 | --admin \ 19 | --login newAdmin \ 20 | --email new-admin@example.com \ 21 | --name "New Admin" \ 22 | --password "New$Password" 23 | ``` 24 | 25 | ## How to change user password 26 | 27 | ``` 28 | semaphore user change-by-login \ 29 | --login myAdmin \ 30 | --password "New$Password" 31 | ``` 32 | -------------------------------------------------------------------------------- /src/administration-guide/cli/vaults.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### Vaults 7 | 8 | You can reencrypt your secrets in database with using following command: 9 | 10 | ``` 11 | semaphore vault rekey --old-key 12 | ``` 13 | 14 | Your data will be decryped using `` and will be encrypted using option `access_key_encryption` from configuration key. 15 | -------------------------------------------------------------------------------- /src/administration-guide/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | Semaphore can be configured using several methods: 4 | 5 | * [Interactive setup](./configuration/snap.md) — guided configuration when running Semaphore for the first time. It creates `config.json`. 6 | * [Configuration file](./configuration/config-file.md) — the primary and most flexible way to configure Semaphore. 7 | * [Envrioment variables](./configuration/env-vars.md) — useful for containerized or cloud-native deployments. 8 | * [Snap configuration (deprecated)](./configuration/snap.md) — legacy method used when installing via Snap packages. 9 | 10 | 11 | ## Configuration options 12 | 13 | Full list of available configuration options: 14 | 15 | | Config file option / Environment variable | Description | 16 | | ----------------------- | --------------------------------------------------------- | 17 | |
`bolt.host`
`SEMAPHORE_DB_HOST`

| Path to the BoltDB database file. | 18 | |
`mysql.host`
`SEMAPHORE_DB_HOST`

| MySQL database host. | 19 | |
`mysql.name`
`SEMAPHORE_DB_NAME`

| MySQL database (schema) name. | 20 | |
`mysql.user`
`SEMAPHORE_DB_USER`

| MySQL user name. | 21 | |
`mysql.pass`
`SEMAPHORE_DB_PASS`

| MySQL user's password. | 22 | |
`postgres.host`
`SEMAPHORE_DB_HOST`

| Postgres database host. | 23 | |
`postgres.name`
`SEMAPHORE_DB_NAME`

| Postgres database (schema) name. | 24 | |
`postgres.user`
`SEMAPHORE_DB_USER`

| Postgres user name. | 25 | |
`postgres.pass`
`SEMAPHORE_DB_PASS`

| Postgres user's password. | 26 | |
`dialect`
`SEMAPHORE_DB_DIALECT`

| Can be `mysql`, `postgres `or `bolt` | 27 | |
`git_client`
`SEMAPHORE_GIT_CLIENT`

| | 28 | |
`ssh_config_path`
`SEMAPHORE_SSH_PATH`

| | 29 | |
`port`
`SEMAPHORE_PORT`

| TCP port on which the web interface will be available. Default: 3000 | 30 | |
`interface`
`SEMAPHORE_INTERFACE`

| Useful if your server has multiple network interfaces | 31 | |
`tmp_path`
`SEMAPHORE_TMP_PATH`

| Path to directory where cloned repositories and generated files are stored. Default: /tmp/semaphore | 32 | |
`access_key_encryption`
`SEMAPHORE_ACCESS_KEY_ENCRYPTION`

| Secret key used for encrypting access keys in database. Read more in [Database encryption reference](./security.md#database-encryption). | 33 | |
`web_host`
`SEMAPHORE_WEB_ROOT`

| Can be useful if you want to use Semaphore by the subpath, for example: [http://yourdomain.com/semaphore](http://yourdomain.com/semaphore). Do not add a trailing `/`. | 34 | |
`tls.enabled`
`SEMAPHORE_TLS_ENABLED`

| | 35 | |
`tls.cert_file`
`SEMAPHORE_TLS_CERT_FILE`

| | 36 | |
`tls.key_file`
`SEMAPHORE_TLS_KEY_FILE`

| | 37 | |
`email_sender`
`SEMAPHORE_EMAIL_SENDER`

| | 38 | |
`email_host`
`SEMAPHORE_EMAIL_HOST`

| | 39 | |
`email_port`
`SEMAPHORE_EMAIL_PORT`

| | 40 | |
`email_secure`
`SEMAPHORE_EMAIL_SECURE`

| | 41 | |
`email_tls`
`SEMAPHORE_EMAIL_TLS`

| | 42 | |
`email_username`
`SEMAPHORE_EMAIL_USERNAME`

| | 43 | |
`email_password`
`SEMAPHORE_EMAIL_PASSWORD`

| | 44 | |
`email_alert`
`SEMAPHORE_EMAIL_ALERT`

| | 45 | |
`telegram_alert`
`SEMAPHORE_TELEGRAM_ALERT`

| Set to True to enable pushing alerts to Telegram. It should be used in combination with `telegram_chat` and `telegram_token`. | 46 | |
`telegram_chat`
`SEMAPHORE_TELEGRAM_CHAT`

| Set to the Chat ID for the chat to send alerts to. Read more in [Telegram Notifications Setup](./notifications.md#chat-id) | 47 | |
`telegram_token`
`SEMAPHORE_TELEGRAM_TOKEN`

| Set to the Authorization Token for the bot that will receive the alert payload. Read more in [Telegram Notifications Setup](./notifications.md#bot-setup) | 48 | |
`slack_alert`
`SEMAPHORE_SLACK_ALERT`

| Set to True to enable pushing alerts to slack. It should be used in combination with `slack_url` | 49 | |
`slack_url`
`SEMAPHORE_SLACK_URL`

| The slack webhook url. Semaphore will used it to POST Slack formatted json alerts to the provided url. | 50 | |
`microsoft_teams_alert`
`SEMAPHORE_MICROSOFT_TEAMS_ALERT`

| Set to True to enable pushing alerts to teams. It should be used in combination with `microsoft_teams_url`. | 51 | |
`microsoft_teams_url`
`SEMAPHORE_MICROSOFT_TEAMS_URL`

| The teams webhook url. Semaphore will used it to POST alerts. | 52 | |
`rocketchat_alert`
`SEMAPHORE_ROCKETCHAT_ALERT`

| Set to True to enable pushing alerts to Rocket.Chat. It should be used in combination with `rocketchat_url`. Available since v2.9.56. | 53 | |
`rocketchat_url`
`SEMAPHORE_ROCKETCHAT_URL`

| The rocketchat webhook url. Semaphore will used it to POST Rocket.Chat formatted json alerts to the provided url. Available since v2.9.56. | 54 | |
`ldap_enable`
`SEMAPHORE_LDAP_ENABLE`

| | 55 | |
`ldap_needtls`
`SEMAPHORE_LDAP_NEEDTLS`

| | 56 | |
`ldap_binddn`
`SEMAPHORE_LDAP_BIND_DN`

| | 57 | |
`ldap_bindpassword`
`SEMAPHORE_LDAP_BIND_PASSWORD`

| | 58 | |
`ldap_server`
`SEMAPHORE_LDAP_SERVER`

| | 59 | |
`ldap_searchdn`
`SEMAPHORE_LDAP_SEARCH_DN`

| | 60 | |
`ldap_searchfilter`
`SEMAPHORE_LDAP_SEARCH_FILTER`

| | 61 | |
`max_parallel_tasks`
`SEMAPHORE_MAX_PARALLEL_TASKS`

| Max allowed parallel tasks for whole Semaphore instance. | 62 | |
`max_task_duration_sec`
`SEMAPHORE_MAX_TASK_DURATION_SEC`

| Max allowed parallel tasks for whole Semaphore instance. | 63 | |
`max_tasks_per_template`
`SEMAPHORE_MAX_TASKS_PER_TEMPLATE`

| Max allowed parallel tasks for whole Semaphore instance. | 64 | |
`oidc_providers` ![Static Badge](https://img.shields.io/badge/v2.10+-red) | OpenID provider settings. You can provide multiple OpenID providers. More about OpenID configuration read in [OpenID](./openid.md).

| 65 | |
`password_login_disable`
`SEMAPHORE_PASSWORD_LOGIN_DISABLED`

![Static Badge](https://img.shields.io/badge/v2.10+-red)

| Disable login with using password. Only LDAP and OpenID. | 66 | |
`non_admin_can_create_project`
`SEMAPHORE_NON_ADMIN_CAN_CREATE_PROJECT`

| | 67 | |
`env_vars`
`SEMAPHORE_ENV_VARS`

| | 68 | |
`forwarded_env_vars`
`SEMAPHORE_FORWARDED_ENV_VARS`

| | 69 | |
`apps`
`SEMAPHORE_APPS`

| | 70 | |
`use_remote_runner`
`SEMAPHORE_USE_REMOTE_RUNNER`

| | 71 | |
`use_remote_runner`
`SEMAPHORE_USE_REMOTE_RUNNER`

| | 72 | |
`runner_registration_token`
`SEMAPHORE_RUNNER_REGISTRATION_TOKEN`

| | 73 | |
`auth.totp.enabled`
`SEMAPHORE_TOTP_ENABLED`

| | 74 | |
`auth.totp.allow_recovery`
`SEMAPHORE_TOTP_ALLOW_RECOVERY`

| | 75 | 76 | ## Frequently asked questions 77 | 78 | ### 1. How to configure a public URL for Semaphore UI 79 | 80 | If you use nginx or other web server before Semaphore, you should provide configuration option `web_host`. 81 | 82 | For example you configured NGINX on the server which proxies queries to Semaphore. 83 | 84 | Server address `https://example.com` and you proxies all queries `https://example.com/semaphore` to Semaphore. 85 | 86 | Your `web_host` will be `https://example.com/semaphore`. 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/administration-guide/configuration/cli.md: -------------------------------------------------------------------------------- 1 | 2 | # Interactive setup 3 | 4 | Use this option for first time configuration (not working for Semaphore installed via Snap). 5 | 6 | ```bash 7 | semaphore setup 8 | ``` -------------------------------------------------------------------------------- /src/administration-guide/configuration/config-file.md: -------------------------------------------------------------------------------- 1 | 2 | # Configuration file 3 | 4 | ## Creating configuration file 5 | 6 | Semaphore uses a `config.json` file for its core configuration. You can generate this file interactively using built-in tools or through a web-based configurator. 7 | 8 | ### Generate via CLI 9 | 10 | Use the following commands to generate the configuration file interactively: 11 | 12 | * For the Semaphore server: 13 | ``` 14 | semaphore setup 15 | ``` 16 | * For the Semaphore runner: 17 | ``` 18 | semaphore runner setup 19 | ``` 20 | 21 |
22 | For more details about runner configuration, see the Runners section. 23 |
24 | 25 | 26 | ### Generate via Web 27 | 28 | Alternatively, you can use the web-based interactive configurator: 29 | * [Server configurator](https://semaphoreui.com/install/binary/2_13/config) 30 | * [Runner configurator](https://semaphoreui.com/install/binary/2_13/runner) 31 | 32 | ## Configuration file example 33 | 34 | Semaphore uses a `config.json` configuration file with following content: 35 | 36 | ```javascript 37 | { 38 | "mysql_test": { 39 | "host": "127.0.0.1:3306", 40 | "user": "root", 41 | "pass": "***", 42 | "name": "semaphore" 43 | }, 44 | 45 | "dialect": "mysql", 46 | 47 | "git_client": "go_git", 48 | 49 | "auth": { 50 | "totp": { 51 | "enabled": false, 52 | "allow_recovery": true 53 | } 54 | }, 55 | 56 | "use_remote_runner": true, 57 | "runner_registration_token": "73fs***", 58 | 59 | "tmp_path": "/tmp/semaphore", 60 | "cookie_hash": "96Nt***", 61 | "cookie_encryption": "x0bs***", 62 | "access_key_encryption": "j1ia***", 63 | 64 | "max_tasks_per_template": 3, 65 | 66 | "log": { 67 | "events": { 68 | "enabled": true, 69 | "path": "./events.log" 70 | } 71 | }, 72 | 73 | "process": { 74 | "chroot": "/opt/semaphore/sandbox" 75 | } 76 | } 77 | ``` 78 | 79 | ## Configuration file usage 80 | 81 | * For Semaphore server: 82 | 83 | ```bash 84 | semaphore server --config ./config.json 85 | ``` 86 | 87 | * For Semaphore runner: 88 | 89 | ```bash 90 | semaphore runner start --config ./config.json 91 | ``` 92 | -------------------------------------------------------------------------------- /src/administration-guide/configuration/env-vars.md: -------------------------------------------------------------------------------- 1 | # Environment variables 2 | 3 | With using environment variables you can override any available configuration option. 4 | 5 | You can use interactive evnvironment variables generator (for Docker): 6 | * for [server](https://semaphoreui.com/install/docker/2_12/) 7 | * for [runner](https://semaphoreui.com/install/docker/2_12/runner). 8 | -------------------------------------------------------------------------------- /src/administration-guide/configuration/snap.md: -------------------------------------------------------------------------------- 1 | # Snap configuration 2 | 3 | Snap configurations should be used for when Semaphore was installed via Snap. 4 | 5 | To see a list of available options, use the following command: 6 | 7 | ```bash 8 | sudo snap get semaphore 9 | ``` 10 | 11 | You can change each of these configurations. For example if you want to change Semaphore port, use following command: 12 | 13 | ```bash 14 | sudo snap set semaphore port=4444 15 | ``` 16 | 17 | Don't forget to restart Semaphore after changing a configuration: 18 | 19 | ```bash 20 | sudo snap restart semaphore 21 | ``` 22 | -------------------------------------------------------------------------------- /src/administration-guide/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | You can install Semaphore in multiple ways, depending on your operating system, environment, and preferences: 4 | 5 | * **Package manager**
6 | Install Semaphore using a native package for your distribution (e.g., apt for Debian/Ubuntu or dnf for RHEL-based systems). This is the easiest way to get started on Linux servers and integrates well with system services.
7 | [Learn more »](./installation/package-manager) 8 | 9 | * **Docker**
10 | Run Semaphore as a container using Docker or Docker Compose. Ideal for fast setup, sandboxed environments, and CI/CD pipelines. Recommended for users who prefer infrastructure as code.
11 | [Learn more »](./installation/docker) 12 | 13 | * **Binary file**
14 | Download a precompiled binary from the releases page. Great for manual installation or embedding in custom workflows. Works across Linux, macOS, and Windows (via WSL).
15 | [Learn more »](./installation/binary-file) 16 | 17 | * **Kubernetes (Helm chart)**
18 | Deploy Semaphore into a Kubernetes cluster using Helm. Best suited for production-grade, scalable infrastructure. Supports easy configuration and upgrades via Helm values.
19 | [Learn more »](./installation/k8s) 20 | 21 | * **Snap (deprecated)**
22 | Previously available as a Snap package. This method is deprecated and no longer maintained. Users are advised to switch to one of the supported methods above.
23 | [Learn more »](./installation/snap) 24 | 25 | See also: 26 | * [Run as service](./installation/binary-file.md#run-as-a-service) 27 | * [Manual installation](./installation_manually.md) 28 | 29 | ---- 30 | 31 | 32 | ### Installing Additional Python Packages 33 | 34 | Some Ansible modules and roles require additional python packages to run. To install additional python packages, create a `requirements.txt` file and mount it in the `/etc/semaphore` directory on the container. For example, you could add the following lines to your `docker-compose.yml` file: 35 | 36 | ```yaml 37 | volumes: 38 | - /path/to/requirements.txt:/etc/semaphore/requirements.txt 39 | ``` 40 | 41 | The packages specified in the requirements file will be installed when the container starts up. 42 | 43 | For more information about Python requirements files, see the [Pip Requirements File Format reference](https://pip.pypa.io/en/stable/reference/requirements-file-format/) 44 | -------------------------------------------------------------------------------- /src/administration-guide/installation/binary-file.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Binary file 7 | 8 |
9 | Look into the manual installation on how to set-up your Python/Ansible/Systemd environment! 10 |
11 | 12 | 13 | Download the `*.tar.gz` for your platform from [Releases page](https://github.com/semaphoreui/semaphore/releases). Unpack it and setup Semaphore using the following commands: 14 | 15 | {{#tabs }} 16 | {{#tab name="Linux (x64)" }} 17 | ``` 18 | download/v2.13.14/semaphore_2.13.14_linux_amd64.tar.gz 19 | 20 | tar xf semaphore_2.13.14_linux_amd64.tar.gz 21 | 22 | ./semaphore setup 23 | ``` 24 | {{#endtab }} 25 | 26 | {{#tab name="Linux (ARM64)" }} 27 | ``` 28 | wget https://github.com/semaphoreui/semaphore/releases/\ 29 | 30 | download/v2.13.14/semaphore_2.13.14_linux_arm64.tar.gz 31 | 32 | tar xf semaphore_2.13.14_linux_arm64.tar.gz 33 | 34 | ./semaphore setup 35 | ``` 36 | {{#endtab }} 37 | 38 | {{#tab name="Windows (x64)" }} 39 | ``` 40 | Invoke-WebRequest ` 41 | -Uri ("https://github.com/semaphoreui/semaphore/releases/" + 42 | "download/v2.13.14/semaphore_2.13.14_windows_amd64.zip") ` 43 | 44 | -OutFile semaphore.zip 45 | 46 | Expand-Archive -Path semaphore.zip -DestinationPath ./ 47 | 48 | ./semaphore setup 49 | ``` 50 | {{#endtab }} 51 | {{#endtabs }} 52 | 53 | Now you can run Semaphore: 54 | 55 | ``` 56 | ./semaphore server --config=./config.json 57 | ``` 58 | 59 | Semaphore will be available via the following URL [https://localhost:3000](https://localhost:3000). 60 | 61 | ---- 62 | 63 | ### Run as a service 64 | 65 | For more detailed information — look into the [extended Systemd service documentation](../installation_manually.md#extended-systemd-service). 66 | 67 | If you installed Semaphore via a package manager, or by downloading a binary file, you should create the Semaphore service manually. 68 | 69 | Create the systemd service file: 70 | 71 |
72 | Replace /path/to/semaphore and /path/to/config.json to your semaphore and config file path. 73 |
74 | 75 | ```bash 76 | sudo cat > /etc/systemd/system/semaphore.service < 3 | Installation 4 | → Docker 5 | 6 | 7 | # Docker 8 | 9 | Create a `docker-compose.yml` file with following content: 10 | 11 | ```yaml 12 | services: 13 | # uncomment this section and comment out the mysql section to use postgres instead of mysql 14 | #postgres: 15 | #restart: unless-stopped 16 | #image: postgres:14 17 | #hostname: postgres 18 | #volumes: 19 | # - semaphore-postgres:/var/lib/postgresql/data 20 | #environment: 21 | # POSTGRES_USER: semaphore 22 | # POSTGRES_PASSWORD: semaphore 23 | # POSTGRES_DB: semaphore 24 | # if you wish to use postgres, comment the mysql service section below 25 | mysql: 26 | restart: unless-stopped 27 | image: mysql:8.0 28 | hostname: mysql 29 | volumes: 30 | - semaphore-mysql:/var/lib/mysql 31 | environment: 32 | MYSQL_RANDOM_ROOT_PASSWORD: 'yes' 33 | MYSQL_DATABASE: semaphore 34 | MYSQL_USER: semaphore 35 | MYSQL_PASSWORD: semaphore 36 | semaphore: 37 | restart: unless-stopped 38 | ports: 39 | - 3000:3000 40 | image: semaphoreui/semaphore:latest 41 | environment: 42 | SEMAPHORE_DB_USER: semaphore 43 | SEMAPHORE_DB_PASS: semaphore 44 | SEMAPHORE_DB_HOST: mysql # for postgres, change to: postgres 45 | SEMAPHORE_DB_PORT: 3306 # change to 5432 for postgres 46 | SEMAPHORE_DB_DIALECT: mysql # for postgres, change to: postgres 47 | SEMAPHORE_DB: semaphore 48 | SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/ 49 | SEMAPHORE_ADMIN_PASSWORD: changeme 50 | SEMAPHORE_ADMIN_NAME: admin 51 | SEMAPHORE_ADMIN_EMAIL: admin@localhost 52 | SEMAPHORE_ADMIN: admin 53 | SEMAPHORE_ACCESS_KEY_ENCRYPTION: gs72mPntFATGJs9qK0pQ0rKtfidlexiMjYCH9gWKhTU= 54 | SEMAPHORE_LDAP_ACTIVATED: 'no' # if you wish to use ldap, set to: 'yes' 55 | SEMAPHORE_LDAP_HOST: dc01.local.example.com 56 | SEMAPHORE_LDAP_PORT: '636' 57 | SEMAPHORE_LDAP_NEEDTLS: 'yes' 58 | SEMAPHORE_LDAP_DN_BIND: 'uid=bind_user,cn=users,cn=accounts,dc=local,dc=shiftsystems,dc=net' 59 | SEMAPHORE_LDAP_PASSWORD: 'ldap_bind_account_password' 60 | SEMAPHORE_LDAP_DN_SEARCH: 'dc=local,dc=example,dc=com' 61 | SEMAPHORE_LDAP_SEARCH_FILTER: "(\u0026(uid=%s)(memberOf=cn=ipausers,cn=groups,cn=accounts,dc=local,dc=example,dc=com))" 62 | TZ: UTC 63 | depends_on: 64 | - mysql # for postgres, change to: postgres 65 | volumes: 66 | semaphore-mysql: # to use postgres, switch to: semaphore-postgres 67 | ``` 68 | 69 | You must specify following confidential variables: 70 | 71 | * `MYSQL_PASSWORD` and `SEMAPHORE_DB_PASS` — password for the MySQL user. 72 | * `SEMAPHORE_ADMIN_PASSWORD` — password for the Semaphore's admin user. 73 | * `SEMAPHORE_ACCESS_KEY_ENCRYPTION` — key for encrypting access keys in database. It must be generated by using the following command: `head -c32 /dev/urandom | base64`. 74 | 75 | If you are using Docker Swarm, it is strongly recommended that you don't embed credentials directly in the Compose file (nor in environment variables generally) and instead use [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/). Semaphore [supports](https://github.com/semaphoreui/semaphore/issues/1268) a common Docker container pattern for retrieving settings from files instead of the environment by appending `_FILE` to the end of the environment variable name. See the [Docker documentation for an example](https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose). 76 | 77 | A limited example using secrets: 78 | 79 | ```yaml 80 | secrets: 81 | semaphore_admin_pw: 82 | file: semaphore_admin_password.txt 83 | 84 | services: 85 | semaphore: 86 | restart: unless-stopped 87 | ports: 88 | - 3000:3000 89 | image: semaphoreui/semaphore:latest 90 | SEMAPHORE_ADMIN_PASSWORD_FILE: /run/secrets/semaphore_admin_pw 91 | SEMAPHORE_ADMIN_NAME: admin 92 | SEMAPHORE_ADMIN_EMAIL: admin@localhost 93 | SEMAPHORE_ADMIN: admin 94 | ``` 95 | 96 | 97 | Run the following command to start Semaphore with configured database (MySQL or Postgres): 98 | 99 | ```bash 100 | docker-compose up 101 | ``` 102 | 103 | Semaphore will be available via the following URL [http://localhost:3000](http://localhost:3000). -------------------------------------------------------------------------------- /src/administration-guide/installation/k8s.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Kubernetes (Helm chart) 7 | 8 | Semaphore provides a helm chart for installation on Kubernetes. 9 | 10 | A thorough documentation can be found on artifacthub.io: [Semaphore Helm Chart](https://artifacthub.io/packages/helm/semaphoreui/semaphore). 11 | -------------------------------------------------------------------------------- /src/administration-guide/installation/package-manager.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Package manager 7 | 8 |
9 | Look into the manual installation on how to set-up your Python/Ansible/Systemd environment! 10 |
11 | 12 | 13 | Download package file from [Releases page](https://github.com/semaphoreui/semaphore/releases). 14 | 15 | `*.deb` for Debian and Ubuntu, `*.rpm` for CentOS and RedHat. 16 | 17 | Here are several installation commands, depending on the package manager: 18 | 19 | 20 | {{#tabs }} 21 | 22 | {{#tab name="Debian / Ubuntu (x64)"}} 23 | ```bash 24 | wget https://github.com/semaphoreui/semaphore/releases/\ 25 | download/v2.13.14/semaphore_2.13.14_linux_amd64.deb 26 | 27 | sudo dpkg -i semaphore_2.13.14_linux_amd64.deb 28 | ``` 29 | {{#endtab }} 30 | 31 | {{#tab name="Debian / Ubuntu (ARM64)" }} 32 | ```bash 33 | wget https://github.com/semaphoreui/semaphore/releases/\ 34 | download/v2.13.14/semaphore_2.13.14_linux_arm64.deb 35 | 36 | sudo dpkg -i semaphore_2.13.14_linux_arm64.deb 37 | ``` 38 | {{#endtab }} 39 | 40 | {{#tab name="CentOS (x64)" }} 41 | ```bash 42 | wget https://github.com/semaphoreui/semaphore/releases/\ 43 | download/v2.13.14/semaphore_2.13.14_linux_amd64.rpm 44 | 45 | sudo yum install semaphore_2.13.14_linux_amd64.rpm 46 | ``` 47 | {{#endtab }} 48 | 49 | {{#tab name="CentOS (ARM64)" }} 50 | ```bash 51 | wget https://github.com/semaphoreui/semaphore/releases/\ 52 | download/v2.13.14/semaphore_2.13.14_linux_arm64.rpm 53 | 54 | sudo yum install semaphore_2.13.14_linux_arm64.rpm 55 | ``` 56 | {{#endtab }} 57 | 58 | {{#endtabs }} 59 | 60 | Setup Semaphore by using the following command: 61 | 62 | ``` 63 | semaphore setup 64 | ``` 65 | 66 | Now you can run Semaphore: 67 | 68 | ``` 69 | semaphore server --config=./config.json 70 | ``` 71 | 72 | Semaphore will be available via this URL [https://localhost:3000](https://localhost:3000). 73 | 74 | ---- 75 | -------------------------------------------------------------------------------- /src/administration-guide/installation/snap.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Snap (deprecated) 7 | 8 | To install Semaphore via snap, run following command in terminal: 9 | 10 | ```bash 11 | sudo snap install semaphore 12 | ``` 13 | 14 | Semaphore will be available by URL [https://localhost:3000](https://localhost:3000). 15 | 16 | But to log in, you should create an admin user. Use the following commands: 17 | 18 | ```bash 19 | sudo snap stop semaphore 20 | 21 | sudo semaphore user add --admin \ 22 | --login john \ 23 | --name=John \ 24 | --email=john1996@gmail.com \ 25 | --password=12345 26 | 27 | sudo snap start semaphore 28 | ``` 29 | 30 | You can check the status of the Semaphore service using the following command: 31 | 32 | ```bash 33 | sudo snap services semaphore 34 | ``` 35 | 36 | It should print the following table: 37 | 38 | ``` 39 | Service Startup Current Notes 40 | semaphore.semaphored enabled active - 41 | ``` 42 | 43 | After installation, you can set up Semaphore via [Snap Configuration](https://snapcraft.io/docs/configuration-in-snaps). Use the following command to see your Semaphore configuration: 44 | 45 | ```bash 46 | sudo snap get semaphore 47 | ``` 48 | 49 | List of available options you can find in [Configuration options reference](../configuration.md#configuration-options). 50 | 51 | ---- 52 | -------------------------------------------------------------------------------- /src/administration-guide/installation_manually.md: -------------------------------------------------------------------------------- 1 | # Manually installing Semaphore 2 | 3 | ---- 4 | 5 | **Content:** 6 | 7 | * [Service User](./installation_manually.md#service-user) 8 | * [Python3](./installation_manually.md#python3) 9 | * [Ansible Collections & Roles](./installation_manually.md#ansible-collections--roles) 10 | * [Reverse Proxy](./installation_manually.md#reverse-proxy) 11 | * [Systemd Service](./installation_manually.md#extended-systemd-service) 12 | * [Troubleshooting](./installation_manually.md#troubleshooting) 13 | 14 | ---- 15 | 16 | This documentation goes into the details on how to set-up Semaphore when using these installation methods: 17 | 18 | * [Package manager](./installation.md#package-manager) 19 | * [Binary file](./installation.md#binary-file) 20 | 21 | The Semaphore software-package is just a part of the whole system needed to successfully run Ansible with it. 22 | 23 | The Python3- and Ansible-Execution-Environment are also very important! 24 | 25 | NOTE: There are [existing Ansible-Galaxy Roles](https://galaxy.ansible.com/search?deprecated=false&keywords=ansible%20semaphore&order_by=-relevance&page=1) that handle this setup-logic for you or can be used as a base-template for your own Ansible Role! 26 | 27 | ---- 28 | 29 | ## Service User 30 | 31 | Semaphore does not need to be run as user `root` - so you shouldn't. 32 | 33 | **Benefits** of using a service user: 34 | * Has its own user-config 35 | * Has its own environment 36 | * Processes easily identifiable 37 | * Gained system security 38 | 39 | You can create a system user either manually by using `adduser` or using the [ansible.builtin.user](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html) module. 40 | 41 | In this documentation we will assume: 42 | * the service user creates is named `semaphore` 43 | * it has the shell `/bin/bash` set 44 | * its home directory is `/home/semaphore` 45 | 46 | ### Troubleshooting 47 | 48 | If the Ansible execution of Semaphore is failing - you will need to troubleshoot it in the context of the service user. 49 | 50 | You have multiple options to do so: 51 | 52 | * Change your whole shell session to be in the user's context: 53 | 54 | ```bash 55 | sudo su --login semaphore 56 | ``` 57 | 58 | * Run a single command in the user's context: 59 | 60 | ```bash 61 | sudo --login -u semaphore 62 | ``` 63 | 64 | ---- 65 | 66 | ## Python3 67 | 68 | [Ansible](https://docs.ansible.com/ansible/latest/getting_started/index.html) is build using the [Python3](https://docs.python.org/3/) programming language. 69 | 70 | So its clean setup is essential for Ansible to work correctly. 71 | 72 | First - make sure the packages `python3` and `python3-pip` are installed on your system! 73 | 74 | You have multiple options to install required Python modules: 75 | * Installing them in the service user's context 76 | * Installing them in a service-specific [Virtual Environment](https://virtualenv.pypa.io/en/latest/) 77 | 78 | ### Requirements 79 | 80 | Either way - it is recommended to use a `requirements.txt` file to specify the modules that need to be installed. 81 | 82 | We will assume the file `/home/semaphore/requirements.txt` is used. 83 | 84 | Here is an example of its content: 85 | 86 | ```text 87 | ansible 88 | # for common jinja-filters 89 | netaddr 90 | jmespath 91 | # for common modules 92 | pywinrm 93 | passlib 94 | requests 95 | docker 96 | ``` 97 | 98 | NOTE: You should also update those requirements from time to time! 99 | 100 | An option for doing this automatically is also shown in the service example below. 101 | 102 | ### Modules in user context 103 | 104 | **Manually**: 105 | 106 | ```bash 107 | sudo --login -u semaphore python3 -m pip install --user --upgrade -r /home/semaphore/requirements.txt 108 | ``` 109 | 110 | **Using Ansible**: 111 | 112 | ```yaml 113 | - name: Install requirements 114 | ansible.builtin.pip: 115 | requirements: '/home/semaphore/requirements.txt' 116 | extra_args: '--user --upgrade' 117 | become_user: 'semaphore' 118 | ``` 119 | 120 | ### Modules in a virtualenv 121 | 122 | We will assume the virtualenv is created at `/home/semaphore/venv` 123 | 124 | Make sure the virtual environment is activated inside the Service! This is also shown in the service example below. 125 | 126 | **Manually**: 127 | ```bash 128 | sudo su --login semaphore 129 | python3 -m pip install --user virtualenv 130 | python3 -m venv /home/semaphore/venv 131 | # activate the context of the virtual environment 132 | source /home/semaphore/venv/bin/activate 133 | # verify we are using python3 from inside the venv 134 | which python3 135 | > /home/semaphore/venv/bin/python3 136 | python3 -m pip install --upgrade -r /home/semaphore/requirements.txt 137 | # disable the context to the virtual environment 138 | deactivate 139 | ``` 140 | 141 | **Using Ansible**: 142 | 143 | ```yaml 144 | - name: Create virtual environment and install requirements into it 145 | ansible.builtin.pip: 146 | requirements: '/home/semaphore/requirements.txt' 147 | virtualenv: '/home/semaphore/venv' 148 | state: present # or 'latest' to upgrade the requirements 149 | ``` 150 | 151 | #### Troubleshooting 152 | 153 | If you encounter Python3 issues when using a virtual environment, you will need to change into its context to troubleshoot them: 154 | 155 | ```bash 156 | sudo su --login semaphore 157 | source /home/semaphore/venv/bin/activate 158 | # verify we are using python3 from inside the venv 159 | which python3 160 | > /home/semaphore/venv/bin/python3 161 | 162 | # troubleshooting 163 | 164 | deactivate 165 | ``` 166 | 167 | Sometimes a virtual environment also breaks on system upgrades. If this happens you might just remove the existing one and re-create it. 168 | 169 | ---- 170 | 171 | ## Ansible Collections & Roles 172 | 173 | You might want to pre-install Ansible modules and roles, so they don't need to be installed every time a task runs! 174 | 175 | ### Requirements 176 | 177 | It is recommended to use a `requirements.yml` file to specify the modules that need to be installed. 178 | 179 | We will assume the file `/home/semaphore/requirements.yml` is used. 180 | 181 | Here is an example of its content: 182 | 183 | ```yaml 184 | --- 185 | 186 | collections: 187 | - 'namespace.collection' 188 | # for common collections: 189 | - 'community.general' 190 | - 'ansible.posix' 191 | - 'community.mysql' 192 | - 'community.crypto' 193 | 194 | roles: 195 | - src: 'namespace.role' 196 | ``` 197 | 198 | See also: [Installing Collections](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-a-collection-from-galaxy), [Installing Roles](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-multiple-roles-from-a-file) 199 | 200 | NOTE: You should also update those requirements from time to time! 201 | 202 | An option for doing this automatically is also shown in the service example below. 203 | 204 | ### Install in user-context 205 | 206 | **Manually**: 207 | ```bash 208 | sudo su --login semaphore 209 | ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml 210 | ansible-galaxy role install --force -r /home/semaphore/requirements.yml 211 | ``` 212 | 213 | ### Install when using a virtualenv 214 | 215 | **Manually**: 216 | ```bash 217 | sudo su --login semaphore 218 | source /home/semaphore/venv/bin/activate 219 | # verify we are using python3 from inside the venv 220 | which python3 221 | > /home/semaphore/venv/bin/python3 222 | 223 | ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml 224 | ansible-galaxy role install --force -r /home/semaphore/requirements.yml 225 | 226 | deactivate 227 | ``` 228 | 229 | ---- 230 | 231 | ## Reverse Proxy 232 | 233 | See: [Security - Encrypted connection](./security.md#reverse-proxy) 234 | 235 | ---- 236 | 237 | ## Extended Systemd Service 238 | 239 | Here is the basic template of the systemd service. 240 | 241 | Add additional settings under their `[PART]` 242 | 243 | ### Base 244 | 245 | ```ini 246 | [Unit] 247 | Description=Semaphore UI 248 | Documentation=https://docs.semaphoreui.com/ 249 | Wants=network-online.target 250 | After=network-online.target 251 | ConditionPathExists=/usr/bin/semaphore 252 | ConditionPathExists=/etc/semaphore/config.json 253 | 254 | [Service] 255 | ExecStart=/usr/bin/semaphore server --config /etc/semaphore/config.json 256 | ExecReload=/bin/kill -HUP $MAINPID 257 | Restart=always 258 | RestartSec=10s 259 | 260 | [Install] 261 | WantedBy=multi-user.target 262 | ``` 263 | 264 | ### Service user 265 | 266 | ```ini 267 | [Service] 268 | User=semaphore 269 | Group=semaphore 270 | ``` 271 | 272 | ---- 273 | 274 | ### Python Modules 275 | 276 | #### In user-context 277 | 278 | ```ini 279 | [Service] 280 | # to auto-upgrade python modules at service startup 281 | ExecStartPre=/bin/bash -c 'python3 -m pip install --upgrade --user -r /home/semaphore/requirements.txt' 282 | 283 | # so the executables are found 284 | Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/semaphore/.local/bin" 285 | # set the correct python path. You can get the correct path with: python3 -c "import site; print(site.USER_SITE)" 286 | Environment="PYTHONPATH=/home/semaphore/.local/lib/python3.10/site-packages" 287 | ``` 288 | 289 | #### In virtualenv 290 | 291 | ```ini 292 | [Service] 293 | # to auto-upgrade python modules at service startup 294 | ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \ 295 | && python3 -m pip install --upgrade -r /home/semaphore/requirements.txt' 296 | 297 | # REPLACE THE EXISTING 'ExecStart' 298 | ExecStart=/bin/bash -c 'source /home/semaphore/venv/bin/activate \ 299 | && /usr/bin/semaphore server --config /etc/semaphore/config.json' 300 | ``` 301 | 302 | ---- 303 | 304 | ### Ansible Collections & Roles 305 | 306 | #### If using Python3 in user-context 307 | 308 | ```ini 309 | [Service] 310 | # to auto-upgrade ansible collections and roles at service startup 311 | ExecStartPre=/bin/bash -c 'ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml' 312 | ExecStartPre=/bin/bash -c 'ansible-galaxy role install --force -r /home/semaphore/requirements.yml' 313 | ``` 314 | 315 | #### If using Python3 in virtualenv 316 | 317 | ```ini 318 | # to auto-upgrade ansible collections and roles at service startup 319 | ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \ 320 | && ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml \ 321 | && ansible-galaxy role install --force -r /home/semaphore/requirements.yml' 322 | ``` 323 | 324 | ---- 325 | 326 | ### Other use-cases 327 | 328 | #### Using local MariaDB 329 | 330 | ```ini 331 | [Unit] 332 | Requires=mariadb.service 333 | ``` 334 | 335 | #### Using local Nginx 336 | 337 | ```ini 338 | [Unit] 339 | Wants=nginx.service 340 | ``` 341 | 342 | #### Sending logs to syslog 343 | 344 | ```ini 345 | [Service] 346 | StandardOutput=journal 347 | StandardError=journal 348 | SyslogIdentifier=semaphore 349 | ``` 350 | 351 | ### Full Examples 352 | 353 | #### Python Modules in user-context 354 | 355 | ```ini 356 | [Unit] 357 | Description=Semaphore UI 358 | Documentation=https://docs.semaphoreui.com/ 359 | Wants=network-online.target 360 | After=network-online.target 361 | ConditionPathExists=/usr/bin/semaphore 362 | ConditionPathExists=/etc/semaphore/config.json 363 | 364 | [Service] 365 | User=semaphore 366 | Group=semaphore 367 | Restart=always 368 | RestartSec=10s 369 | Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:~/.local/bin" 370 | 371 | ExecStartPre=/bin/bash -c 'ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml' 372 | ExecStartPre=/bin/bash -c 'ansible-galaxy role install --force -r /home/semaphore/requirements.yml' 373 | ExecStartPre=/bin/bash -c 'python3 -m pip install --upgrade --user -r /home/semaphore/requirements.txt' 374 | 375 | ExecStart=/usr/bin/semaphore server --config /etc/semaphore/config.json 376 | ExecReload=/bin/kill -HUP $MAINPID 377 | 378 | [Install] 379 | WantedBy=multi-user.target 380 | ``` 381 | 382 | #### Python Modules in virtualenv 383 | 384 | ```ini 385 | [Unit] 386 | Description=Semaphore UI 387 | Documentation=https://docs.semaphoreui.com/ 388 | Wants=network-online.target 389 | After=network-online.target 390 | ConditionPathExists=/usr/bin/semaphore 391 | ConditionPathExists=/etc/semaphore/config.json 392 | 393 | [Service] 394 | User=semaphore 395 | Group=semaphore 396 | Restart=always 397 | RestartSec=10s 398 | 399 | ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \ 400 | && python3 -m pip install --upgrade -r /home/semaphore/requirements.txt' 401 | ExecStartPre=/bin/bash -c 'source /home/semaphore/venv/bin/activate \ 402 | && ansible-galaxy collection install --upgrade -r /home/semaphore/requirements.yml \ 403 | && ansible-galaxy role install --force -r /home/semaphore/requirements.yml' 404 | 405 | ExecStart=/bin/bash -c 'source /home/semaphore/venv/bin/activate \ 406 | && /usr/bin/semaphore server --config /etc/semaphore/config.json' 407 | ExecReload=/bin/kill -HUP $MAINPID 408 | 409 | [Install] 410 | WantedBy=multi-user.target 411 | ``` 412 | 413 | ### Fixes 414 | 415 | If you have a custom system language set - you might run into problems that can be resoled by updating the associated environmental variables: 416 | 417 | ```ini 418 | [Service] 419 | Environment=LANG="en_US.UTF-8" 420 | Environment=LC_ALL="en_US.UTF-8" 421 | ``` 422 | 423 | ---- 424 | 425 | ## Troubleshooting 426 | 427 | If there is a problem while executing a task it might be an environmental issue with your setup - not an issue with Semaphore itself! 428 | 429 | Please go through these steps to verify if the issue occurs outside Semaphore: 430 | 431 | - Change into the context of the user: 432 | 433 | ```bash 434 | sudo su --login semaphore 435 | ``` 436 | 437 | - Change into the context of the virtualenv if you use one: 438 | 439 | ```ini 440 | source /home/semaphore/venv/bin/activate 441 | # verify we are using python3 from inside the venv 442 | which python3 443 | > /home/semaphore/venv/bin/python3 444 | 445 | # troubleshooting 446 | 447 | deactivate 448 | ``` 449 | 450 | - Run the Ansible Playbook manually 451 | 452 | - If it **fails** => there is an issue with your environment 453 | - If it **works**: 454 | - Re-check your configuration inside Semaphore 455 | - It might be an issue with Semaphore 456 | -------------------------------------------------------------------------------- /src/administration-guide/ldap.md: -------------------------------------------------------------------------------- 1 | # LDAP configuration 2 | 3 | Configuration file contains the following LDAP parameters: 4 | 5 | ```json 6 | { 7 | "ldap_binddn": "cn=admin,dc=example,dc=org", 8 | "ldap_bindpassword": "admin_password", 9 | "ldap_server": "localhost:389", 10 | "ldap_searchdn": "ou=users,dc=example,dc=org", 11 | "ldap_searchfilter": "(&(objectClass=inetOrgPerson)(uid=%s))", 12 | "ldap_mappings": { 13 | "dn": "", 14 | "mail": "uid", 15 | "uid": "uid", 16 | "cn": "cn" 17 | }, 18 | "ldap_enable": true, 19 | "ldap_needtls": false, 20 | } 21 | ``` 22 | 23 | All SSO provider options: 24 | 25 | | Parameter | Environment Variables | Description | 26 | | --------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------- | 27 | | `ldap_binddn` | `SEMAPHORE_LDAP_BIND_DN` | | 28 | | `ldap_bindpassword` | `SEMAPHORE_LDAP_BIND_PASSWORD` | Password of LDAP user which used as Bind DN. | 29 | | `ldap_server` | `SEMAPHORE_LDAP_SERVER` | LDAP server host including port. For example: `localhost:389`. | 30 | | `ldap_searchdn` | `SEMAPHORE_LDAP_SEARCH_DN` | Scope where users will be searched. For example: `ou=users,dc=example,dc=org`. | 31 | | `ldap_searchfilter` | `SEMAPHORE_LDAP_SEARCH_FILTER` | Users search expression. Default: `(&(objectClass=inetOrgPerson)(uid=%s))`, where `%s` will replaced to entered login. | 32 | | `ldap_mappings.dn` | `SEMAPHORE_LDAP_MAPPING_DN` | | 33 | | `ldap_mappings.mail` | `SEMAPHORE_LDAP_MAPPING_MAIL` | User email claim expression[\*](#claim-expression). | 34 | | `ldap_mappings.uid` | `SEMAPHORE_LDAP_MAPPING_UID` | User login claim expression[\*](#claim-expression). | 35 | | `ldap_mappings.cn` | `SEMAPHORE_LDAP_MAPPING_CN` | User name claim expression[\*](#claim-expression). | 36 | | `ldap_enable` | `SEMAPHORE_LDAP_ENABLE` | LDAP enabled. | 37 | | `ldap_needtls` | `SEMAPHORE_LDAP_NEEDTLS` | Connect to LDAP server by SSL. | 38 | 39 | 40 | ### \*Claim expression 41 | 42 | Example of claim expression: 43 | 44 | ``` 45 | email | {{ .username }}@your-domain.com 46 | ``` 47 | 48 | Semaphore is attempting to claim the email field first. If it is empty, the expression following it is executed. 49 | 50 |
51 | The expression "username_claim": "|" generates a random username for each user who logs in through the provider. 52 |
53 | 54 | ### Troubleshooting 55 | 56 | Use `ldapwhoami` tool to check if your **BindDN** works: 57 | 58 | ```bash 59 | ldapwhoami\ 60 | -H ldap://ldap.com:389\ 61 | -D "CN=your_ldap_binddn_value_in_config"\ 62 | -x\ 63 | -W 64 | ``` 65 | 66 | It will ask interactively for the password, and should return code **0** and echo out the **DN** as specified. 67 | 68 |
69 | Please read Troubleshooting section if you have issues with LDAP. 70 |
71 | 72 | 73 | ## Example: Using OpenLDAP Server 74 | 75 | Run the following command to start your own LDAP server with an admin account and an additional user: 76 | 77 | ``` 78 | docker run -d --name openldap \ 79 | -p 1389:1389 \ 80 | -p 1636:1636 \ 81 | -e LDAP_ADMIN_USERNAME=admin \ 82 | -e LDAP_ADMIN_PASSWORD=pwd \ 83 | -e LDAP_USERS=user1 \ 84 | -e LDAP_PASSWORDS=pwd \ 85 | -e LDAP_ROOT=dc=example,dc=org \ 86 | -e LDAP_ADMIN_DN=cn=admin,dc=example,dc=org \ 87 | bitnami/openldap:latest 88 | ``` 89 | 90 | Your LDAP configuration for Semaphore UI should be as follows: 91 | 92 | ```json 93 | { 94 | "ldap_binddn": "cn=admin,dc=example,dc=org", 95 | "ldap_bindpassword": "pwd", 96 | "ldap_server": "ldap-server.com:1389", 97 | "ldap_searchdn": "dc=example,dc=org", 98 | "ldap_searchfilter": "(&(objectClass=inetOrgPerson)(uid=%s))", 99 | "ldap_mappings": { 100 | "mail": "{{ .cn }}@ldap.your-domain.com", 101 | "uid": "|", 102 | "cn": "cn" 103 | }, 104 | "ldap_enable": true, 105 | "ldap_needtls": false 106 | } 107 | ``` 108 | 109 | To run Semaphore in Docker, use the following LDAP configuration: 110 | 111 | 112 | ``` 113 | docker run -d -p 3000:3000 --name semaphore \ 114 | -e SEMAPHORE_DB_DIALECT=bolt \ 115 | -e SEMAPHORE_ADMIN=admin \ 116 | -e SEMAPHORE_ADMIN_PASSWORD=changeme \ 117 | -e SEMAPHORE_ADMIN_NAME=Admin \ 118 | -e SEMAPHORE_ADMIN_EMAIL=admin@localhost \ 119 | -e SEMAPHORE_LDAP_ENABLE=yes \ 120 | -e SEMAPHORE_LDAP_SERVER=ldap-server.com:1389 \ 121 | -e SEMAPHORE_LDAP_BIND_DN=cn=admin,dc=example,dc=org \ 122 | -e SEMAPHORE_LDAP_BIND_PASSWORD=pwd \ 123 | -e SEMAPHORE_LDAP_SEARCH_DN=dc=example,dc=org \ 124 | -e 'SEMAPHORE_LDAP_SEARCH_FILTER=(&(objectClass=inetOrgPerson)(uid=%s))' \ 125 | -e 'SEMAPHORE_LDAP_MAPPING_MAIL={{ .cn }}@ldap.your-domain.com' \ 126 | -e 'SEMAPHORE_LDAP_MAPPING_UID=|' \ 127 | -e 'SEMAPHORE_LDAP_MAPPING_CN=cn' \ 128 | semaphoreui/semaphore:latest 129 | ``` 130 | 131 | -------------------------------------------------------------------------------- /src/administration-guide/logs.md: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | Semaphore writes server logs to **stdout** and stores **Task** and **Activity** logs in a **database**, centralizing key log information and eliminating the need to back up log files separately. The only data stored on the file system is caching data. 4 | 5 | --- 6 | 7 | ## Server log 8 | 9 | Semaphore does not log to files. Instead, all application logs are written to **stdout**. 10 | If Semaphore is running as a systemd service, you can view the logs with the following command: 11 | 12 | ```bash 13 | journalctl -u semaphore.service -f 14 | ``` 15 | 16 | This provides a live (streaming) view of the logs. 17 | 18 | --- 19 | 20 | ## Activity log 21 | 22 | The Activity Log captures all user actions performed in Semaphore, including: 23 | 24 | - Adding or removing resources (e.g., Templates, Inventories, Repositories). 25 | - Adding or removing team members. 26 | - Starting or stopping tasks. 27 | 28 | ### Pro version 2.10 and later 29 | 30 | **Semaphore Pro** 2.10+ supports writing the Activity Log and Task log to a file. To enable this, add the following configuration to your `config.json`: 31 | 32 | ```json 33 | { 34 | "log": { 35 | "events": { 36 | "enabled": true, 37 | "logger": { 38 | "filename": "./events.log" 39 | // other logger options 40 | } 41 | }, 42 | "tasks": { 43 | "enabled": true, 44 | "logger": { 45 | "filename": "./tasks.log" 46 | // other logger options 47 | } 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | 54 | Or you can do this using following environment variables: 55 | 56 | ```bash 57 | export SEMAPHORE_EVENT_LOG_ENABLED=True 58 | export SEMAPHORE_EVENT_LOG_PATH=./events.log 59 | 60 | export SEMAPHORE_TASK_LOG_ENABLED=True 61 | export SEMAPHORE_TASK_LOG_PATH=./tasks.log 62 | ``` 63 | 64 | #### Events logging options 65 | 66 | | Parameter | Environment Variables | Description | 67 | | --------------------- | --------------------- | --------------------- | 68 | | `enabled` | `SEMAPHORE_EVENT_LOG_ENABLED` | Enable event logging to file. | 69 | | `format` | `SEMAPHORE_EVENT_LOG_FORMAT` | Log record format. Can be `raw` or `json`. | 70 | | `logger` | `SEMAPHORE_EVENT_LOG_LOGGER` | Logger opitons. | 71 | 72 | #### Tasks logging options 73 | 74 | | Parameter | Environment Variables | Description | 75 | | --------------------- | --------------------- | --------------------- | 76 | | `enabled` | `SEMAPHORE_TASK_LOG_ENABLED` | Enable task logging to file. | 77 | | `format` | `SEMAPHORE_TASK_LOG_FORMAT` | Log record format. Can be `raw` or `json`. | 78 | | `logger` | `SEMAPHORE_TASK_LOG_LOGGER` | Logger opitons. | 79 | 80 | #### Logger options 81 | 82 | | Parameter | Type | Description | 83 | | --------------------- | ------- | --------------------- | 84 | | `filename` | String | Path and name of the file to write logs to. Backup log files will be retained in the same directory. It uses -lumberjack.log in temporary if empty. | 85 | | `maxsize` | Integer | The maximum size in megabytes of the log file before it gets rotated. It defaults to 100 megabytes. | 86 | | `maxage` | Integer | The maximum number of days to retain old log files based on the timestamp encoded in their filename. Note that a day is defined as 24 hours and may not exactly correspond to calendar days due to daylight savings, leap seconds, etc. The default is not to remove old log files based on age. | 87 | | `maxbackups` | Integer | The maximum number of old log files to retain. The default is to retain all old log files (though MaxAge may still cause them to get deleted.) | 88 | | `localtime` | Boolean | Determines if the time used for formatting the timestamps in backup files is the computer's local time. The default is to use UTC time. | 89 | | `compress` | Boolean | Determines if the rotated log files should be compressed using gzip. The default is not to perform compression. | 90 | 91 | 92 | 93 | Each line in the file follows this format: 94 | 95 | ``` 96 | 2024-01-03 12:00:34 user=234234 object=template action=delete 97 | ``` 98 | 99 | --- 100 | 101 | ## Task history 102 | 103 | Semaphore stores information about task execution in the database. Task history provides a detailed view of all executed tasks, including their status and logs. You can monitor tasks in real time or review historical logs through the web interface. 104 | 105 | ### Configuring task retention 106 | 107 | By default, Semaphore stores all tasks in the database. If you run a large number of tasks, the can occupy a significant amount of disk space. 108 | 109 | You can configure how many tasks are retained per template using one of the following approaches: 110 | 111 | 1. **Environment Variable** 112 | ```bash 113 | SEMAPHORE_MAX_TASKS_PER_TEMPLATE=30 114 | ``` 115 | 2. **`config.json` Option** 116 | ```json 117 | { 118 | "max_tasks_per_template": 30 119 | } 120 | ``` 121 | 122 | When the number of tasks exceeds this limit, the oldest Task Logs are automatically deleted. 123 | 124 | --- 125 | 126 | ## Summary 127 | 128 | - **Server log:** Written to stdout; viewable via `journalctl` if running under systemd. 129 | - **Activity and tasks log:** Tracks all user actions. Optionally, **Pro 2.10+** can write these to a file. 130 | - **Task history:** Stores real-time and historical task execution logs. Retention is configurable per template. 131 | 132 | Following these guidelines ensures you have proper visibility into Semaphore UI operations while controlling storage usage and log retention. -------------------------------------------------------------------------------- /src/administration-guide/notifications.md: -------------------------------------------------------------------------------- 1 | # Notifications 2 | 3 | Semaphore UI supports following notifications: 4 | 5 | * email 6 | * slack 7 | * telegram 8 | 9 | ## Telegram 10 | 11 | ### Pre-requisites 12 | 13 | In order to configure Semaphore UI to send alerts via Telegram, a few steps are required beforehand on the Telegram side. You'll need to create your own bot that will receive the webhook and you'll need to know the ID of the chat you want to send the message to. 14 | 15 | #### Bot setup 16 | 17 | The easiest way to set up your own bot is to use @BotFather. 18 | 19 | 1. In your Telegram client, message @BotFather with `/start`. 20 | 1. Follow the prompts to create a new bot and take note of the Authorization Token given in the last step. Note: this token is secret and should be treated as such. 21 | 1. Message your new bot with `/start` to start the bot so it can receive messages. 22 | 23 | #### Chat ID 24 | 25 | 1. In your Telegram client, message @RawDataBot with any message. 26 | 1. Copy the value for the `id` key in the `chat` map. 27 | 28 | #### Testing 29 | 30 | You can use cURL to validate your settings above as follows: 31 | 32 | ``` 33 | curl -X POST https://api.telegram.org/botYOUR_BOT_TOKEN/sendMessage \ 34 | -d chat_id=YOUR_CHAT_ID \ 35 | -d text="Test message from curl" 36 | ``` 37 | 38 | ### Configuration 39 | 40 | Using the Chat ID and Token from the previous steps, you can now configure Semaphore UI to send Telegram Alerts as follows: 41 | 42 | ``` 43 | telegram_alert: True 44 | telegram_chat: 45 | telegram_token: 46 | ``` 47 | 48 | ### Per-project Chat IDs 49 | 50 | Each project can use a unique Chat ID. This allows you to separate notifications by project rather than have them all go to the same chat. This overrides the global Chat ID from above. 51 | 52 | -------------------------------------------------------------------------------- /src/administration-guide/openid.md: -------------------------------------------------------------------------------- 1 | # OpenID 2 | 3 | Semaphore supports authentication via OpenID Connect (OIDC). 4 | 5 | Links: 6 | 7 | * [GitHub config](./openid/github.md) 8 | * [Google config](./openid/google.md) 9 | * [GitLab config](./openid/gitlab.md) 10 | * [Authelia config](./openid/authelia.md) 11 | * [Authentik config](./openid/authentik.md) 12 | * [Keycloak config](./openid/keycloak.md) 13 | * [Okta config](./openid/okta.md) 14 | * [Azure config](./openid/azure.md) 15 | 16 | Example of SSO provider configuration: 17 | 18 | ```json 19 | { 20 | "oidc_providers": { 21 | "mysso": { 22 | "display_name": "Sign in with MySSO", 23 | "color": "orange", 24 | "icon": "login", 25 | "provider_url": "https://mysso-provider.com", 26 | "client_id": "***", 27 | "client_secret": "***", 28 | "redirect_url": "https://your-domain.com/api/auth/oidc/mysso/redirect" 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | All SSO provider options: 35 | 36 | | Parameter | Description | 37 | | --------------------- | ----------------------------------------------------------------------------------------------------------- | 38 | | `display_name` | Provider name which displayed on Login screen. | 39 | | `icon` | [MDI-icon](https://pictogrammers.com/library/mdi/) which displayed before of provider name on Login screen. | 40 | | `color` | Provider name which displayed on Login screen. | 41 | | `client_id` | Provider client ID. | 42 | | `client_id_file` | The path to the file where the provider's client ID is stored. Has less priorty then `client_id`. | 43 | | `client_secret` | Provider client Secret. | 44 | | `client_secret_file` | The path to the file where the provider's client secret is stored. Has less priorty then `client_secret`. | 45 | | `redirect_url` | | 46 | | `provider_url` | | 47 | | `scopes` | | 48 | | `username_claim` | Username claim expression[\*](#claim-expression). | 49 | | `email_claim` | Email claim expression[\*](#claim-expression). | 50 | | `name_claim` | Profile Name claim expression[\*](#claim-expression). | 51 | | `order` | Position of the provider button on the Sign in screen. | 52 | | `endpoint.issuer` | | 53 | | `endpoint.auth` | | 54 | | `endpoint.token` | | 55 | | `endpoint.userinfo` | | 56 | | `endpoint.jwks` | | 57 | | `endpoint.algorithms` | | 58 | 59 | ### \*Claim expression 60 | 61 | Example of claim expression: 62 | 63 | ``` 64 | email | {{ .username }}@your-domain.com 65 | ``` 66 | 67 | Semaphore is attempting to claim the email field first. If it is empty, the expression following it is executed. 68 | 69 |
70 | The expression "username_claim": "|" generates a random username for each user who logs in through the provider. 71 |
72 | 73 | ## Sign in screen 74 | 75 | For each of the configured providers, an additional login button is added to the login page: 76 | 77 | ![Screenshot of the Semaphore login page, with two login buttons. One says "Sign In", the other says "Sign in with MySSO"](https://user-images.githubusercontent.com/5564491/232345599-13f744a0-0530-4422-8b55-6a563a4ef5d9.png) 78 | -------------------------------------------------------------------------------- /src/administration-guide/openid/authelia.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Authelia config 7 | 8 | Authelia `config.yaml`: 9 | ```yaml 10 | identity_providers: 11 | oidc: 12 | claims_policies: 13 | semaphore_claims_policy: 14 | id_token: 15 | - groups 16 | - email 17 | - email_verified 18 | - alt_emails 19 | - preferred_username 20 | - name 21 | clients: 22 | - client_id: semaphore 23 | client_name: Semaphore 24 | client_secret: 'your_secret' 25 | claims_policy: semaphore_claims_policy 26 | public: false 27 | authorization_policy: two_factor 28 | redirect_uris: 29 | - https://your-semaphore-domain.com/api/auth/oidc/authelia/redirect 30 | scopes: 31 | - openid 32 | - profile 33 | - email 34 | userinfo_signed_response_alg: none 35 | ``` 36 | 37 | Semaphore `config.json`: 38 | ```json 39 | "oidc_providers": { 40 | "authelia": { 41 | "display_name": "Authelia", 42 | "provider_url": "https://your-authelia-domain.com", 43 | "client_id": "semaphore", 44 | "client_secret": "your_secret", 45 | "redirect_url": "https://your-semaphore-domain.com/api/auth/oidc/authelia/redirect" 46 | } 47 | }, 48 | ``` 49 | -------------------------------------------------------------------------------- /src/administration-guide/openid/authentik.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Authentik config 7 | 8 | `config.json`: 9 | 10 | ```yaml 11 | { 12 | "oidc_providers": { 13 | "authentik": { 14 | "display_name": "Sign in with Authentik", 15 | "provider_url": "https://authentik.example.com/application/o//", 16 | "client_id": "", 17 | "client_secret": "", 18 | "redirect_url": "https://semaphore.example.com/api/auth/oidc/authentik/redirect/", 19 | "scopes": ["openid", "profile", "email"], 20 | "username_claim": "preferred_username", 21 | "name_claim": "preferred_username" 22 | } 23 | } 24 | } 25 | ``` 26 | 27 | Discussion on GitHub: [#1663](https://github.com/semaphoreui/semaphore/discussions/1663). 28 | 29 | See also description in [authentik docs](https://docs.goauthentik.io/integrations/services/semaphore/). 30 | -------------------------------------------------------------------------------- /src/administration-guide/openid/azure.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Azure config 7 | 8 | `config.json`: 9 | ```json 10 | { 11 | "oidc_providers": { 12 | "azure": { 13 | "color": "blue", 14 | "display_name": "Sign in with Azure (Entra ID)", 15 | "provider_url": "https://login.microsoftonline.com//v2.0", 16 | "client_id": "", 17 | "client_secret": "", 18 | "redirect_url": "https://semaphore.test.com/api/auth/oidc/azure/redirect" 19 | } 20 | } 21 | } 22 | ``` -------------------------------------------------------------------------------- /src/administration-guide/openid/gitea.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Gitea config 7 | 8 | `config.json`: 9 | ```json 10 | "oidc_providers": { 11 | "github": { 12 | "icon": "github", 13 | "display_name": "Sign in with gitea instance", 14 | "client_id": "123-456-789", 15 | "client_secret": "**********", 16 | "redirect_url": "https://your-semaphore.tld/api/auth/oidc/github/redirect", 17 | "endpoint": { 18 | "auth": "https://your-gitea.tld/login/oauth/authorize", 19 | "token": "https://your-gitea.tld/login/oauth/access_token", 20 | "userinfo": "https://your-gitea.tld/api/v1/user" 21 | }, 22 | "scopes": ["read:user", "user:email"], 23 | "username_claim": "login", 24 | "email_claim": "email", 25 | "name_claim": "full_name", 26 | "order": 1 27 | } 28 | } 29 | ``` 30 | 31 | In your `gitea` instance, go to `https://your-gitea.tld/user/settings/applications` and create a new `oauth2` application. 32 | As redirect URI use `https://your-semaphore.tld/api/auth/oidc/github/redirect`. 33 | 34 | Authentication works fine. But "Name" and "Username" does not recieved correctly. The username will be a unique ID in semaphore and the name will be set to "Anonymous", which is changeable by the user itself. The emails is mapped correctly. 35 | -------------------------------------------------------------------------------- /src/administration-guide/openid/github.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # GitHub config 7 | 8 | `config.json`: 9 | ```json 10 | { 11 | "oidc_providers": { 12 | "github": { 13 | "icon": "github", 14 | "display_name": "Sign in with GitHub", 15 | "client_id": "***", 16 | "client_secret": "***", 17 | "redirect_url": "https://your-domain.com/api/auth/oidc/github/redirect", 18 | "endpoint": { 19 | "auth": "https://github.com/login/oauth/authorize", 20 | "token": "https://github.com/login/oauth/access_token", 21 | "userinfo": "https://api.github.com/user" 22 | }, 23 | "scopes": ["read:user", "user:email"], 24 | "username_claim": "|", 25 | "email_claim": "email | {{ .id }}@github.your-domain.com", 26 | "name_claim": "name", 27 | "order": 1 28 | } 29 | } 30 | } 31 | ``` -------------------------------------------------------------------------------- /src/administration-guide/openid/gitlab.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # GitLab config 7 | 8 | `config.json`: 9 | ```json 10 | { 11 | "oidc_providers": { 12 | "gitlab": { 13 | "display_name": "Sign in with GitLab", 14 | "color": "orange", 15 | "icon": "gitlab", 16 | "provider_url": "https://gitlab.com", 17 | "client_id": "***", 18 | "client_secret": "gloas-***", 19 | "redirect_url": "https://your-domain.com/api/auth/oidc/gitlab/redirect", 20 | "username_claim": "|", 21 | "order": 3 22 | } 23 | } 24 | } 25 | ``` 26 | 27 | Tutorial in Semaphore UI blog: [GitLab authentication in Semaphore UI](https://semaphoreui.com/blog/openid-authentication/). -------------------------------------------------------------------------------- /src/administration-guide/openid/google.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Google config 7 | 8 | `config.json`: 9 | ```json 10 | { 11 | "oidc_providers": { 12 | "google": { 13 | "color": "blue", 14 | "icon": "google", 15 | "display_name": "Sign in with Google", 16 | "provider_url": "https://accounts.google.com", 17 | "client_id": "***.apps.googleusercontent.com", 18 | "client_secret": "GOCSPX-***", 19 | "redirect_url": "https://your-domain.com/api/auth/oidc/google/redirect", 20 | "username_claim": "|", 21 | "name_claim": "name", 22 | "order": 2 23 | } 24 | } 25 | } 26 | ``` -------------------------------------------------------------------------------- /src/administration-guide/openid/keycloak.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Keycloak config 7 | 8 | `config.json`: 9 | 10 | ```yaml 11 | { 12 | "oidc_providers": { 13 | "keycloak": { 14 | "display_name": "Sign in with keycloak", 15 | "provider_url": "https://keycloak.example.com/realms/master", 16 | "client_id": "***", 17 | "client_secret": "***", 18 | "redirect_url": "https://semaphore.example.com/api/auth/oidc/keycloak/redirect" 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | 25 | ## Related GitHub Issues 26 | 27 | * [#2308](https://github.com/semaphoreui/semaphore/issues/2308) — How to disable certificate validation for Keycloak server 28 | * [#2314](https://github.com/semaphoreui/semaphore/issues/2314) — Option to disable TLS verification 29 | * [#1496](https://github.com/semaphoreui/semaphore/issues/1496) — Log out from Keycloak session when logging out from Semaphore 30 | 31 | [Explore all Keycloak-related issues →](https://github.com/semaphoreui/semaphore/issues?q=is%3Aissue%20keycloak) 32 | 33 | ## Related GitHub Discussions 34 | 35 | * [#1745](https://github.com/semaphoreui/semaphore/discussions/1745) — Username differs from `preferred_username` in OpenID 36 | * [#1030](https://github.com/semaphoreui/semaphore/discussions/1030) — SAML support? 37 | 38 | [Explore all Keycloak-related discussions →](https://github.com/semaphoreui/semaphore/discussions?discussions_q=keycloak) -------------------------------------------------------------------------------- /src/administration-guide/openid/okta.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Okta config 7 | 8 | `config.json`: 9 | 10 | ```yaml 11 | { 12 | "oidc_providers": { 13 | "okta": { 14 | "display_name":"Sign in with Okta", 15 | "provider_url":"https://trial-776xxxx.okta.com/oauth2/default", 16 | "client_id":"***", 17 | "client_secret":"***", 18 | "redirect_url":"https://semaphore.example.com/api/auth/oidc/okta/redirect/" 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | 25 | ## Related GitHub Issues 26 | 27 | * [#1434](https://github.com/semaphoreui/semaphore/issues/1434) — Help with OIDC Azure AD configuration/debugging 28 | * [#1864](https://github.com/semaphoreui/semaphore/issues/1864) — v2.9.56 breaks oidc auth with keycloak 29 | * [#1329](https://github.com/semaphoreui/semaphore/issues/1329) — testing oidc_providers 30 | 31 | [Explore all Keycloak-related issues →](https://github.com/semaphoreui/semaphore/issues?q=is%3Aissue%20okta) 32 | 33 | ## Related GitHub Discussions 34 | 35 | * [#2822](https://github.com/semaphoreui/semaphore/discussions/2822) — When setting up GitHub OpenID, parsing is not possible except for Email 36 | * [#1030](https://github.com/semaphoreui/semaphore/discussions/1030) — SAML support? 37 | 38 | [Explore all Keycloak-related discussions →](https://github.com/semaphoreui/semaphore/discussions?discussions_q=okta) -------------------------------------------------------------------------------- /src/administration-guide/runners.md: -------------------------------------------------------------------------------- 1 | # Runners 2 | 3 | Runners enable running tasks on a separate server from Semaphore UI. 4 | 5 | Semaphore runners operate on the same principle as GitLab or GitHub Actions runners: 6 | 7 | - You launch a runner on a separate server, specifying the Semaphore server's address and an authentication token. 8 | - The runner connects to Semaphore and signals its readiness to accept tasks. 9 | - When a new task appears, Semaphore provides all the necessary information to the runner, which, in turn, clones the repository and runs Ansible, Terraform, PowerShell, etc. 10 | - The runner sends the task execution results back to Semaphore. 11 | 12 | For end users, working with Semaphore with or without runners appears the same. 13 | 14 | Using runners offers the following advantages: 15 | - Executing tasks more securely. For instance, a runner can be located within a closed subnet or isolated docker container. 16 | - Distributing the workload across multiple servers. You can start multiple runners, and tasks will be randomly distributed among them. 17 | 18 | ## Set up 19 | 20 | ### Set up a server 21 | 22 | To set up the server for working with running you should add following option to your Semaphore server configuration: 23 | 24 | ```json 25 | { 26 | "use_remote_runner": true, 27 | "runner_registration_token": "long string of random characters" 28 | } 29 | ``` 30 | 31 | or with using environment variables: 32 | 33 | ```bash 34 | SEMAPHORE_USE_REMOTE_RUNNER=True 35 | SEMAPHORE_RUNNER_REGISTRATION_TOKEN=long_string_of_random_characters 36 | ``` 37 | 38 | ### Setup a runner 39 | 40 | To set up the runner, use the following command: 41 | 42 | ```bash 43 | semaphore runner setup --config /path/to/your/config/file.json 44 | ``` 45 | 46 | This command will create a configuration file at `/path/to/your/config/file.json`. 47 | 48 | But before using this command, you need to understand how runners are registered on the server. 49 | 50 | ### Registering the runner on the server 51 | 52 | There are two ways to register a runner on the Semaphore server: 53 | 1) Add it via the web interface or API. 54 | 2) Use the command line with the `semaphore runner register` command. 55 | 56 | #### Adding the runner via the web UI 57 | 58 | 59 | 60 | #### Registering via CLI 61 | 62 | To register a runner this way, you need to add the `runner_registration_token` option to your Semaphore server's configuration file. This option should be set to an arbitrary string. Choose a sufficiently complex string to avoid security issues. 63 | 64 | When the `semaphore runner setup` command asks if you have a Runner token, answer No. Then use the following command to register the runner: 65 | 66 | `semaphore runner register --config /path/to/your/config/file.json` 67 | 68 | or 69 | 70 | `echo REGISTRATION_TOKEN | semaphore runner register --stdin-registration-token --config /path/to/your/config/file.json` 71 | 72 | ### Configuration file 73 | 74 | As a result of running the `semaphore runner setup` command, a configuration file like the following will be created: 75 | 76 | ```json 77 | { 78 | "tmp_path": "/tmp/semaphore", 79 | "web_host": "https://semaphore_server_host", 80 | 81 | // Here you can provide other settings, for example: git_client, ssh_config_path, etc. 82 | // ... 83 | 84 | // Runner specific options 85 | "runner": { 86 | "token": "your runner's token", 87 | // or 88 | "token_file": "path/to/the/file/where/runner/saves/token" 89 | 90 | // Here you can provide other runner-specific options, 91 | // which will be used for runner registration, for example: 92 | // max_parallel_tasks, webhook, one_off, etc. 93 | // ... 94 | } 95 | } 96 | ``` 97 | 98 | You can manually edit this file without needing to call `semaphore runner setup` again. 99 | 100 | To re-register the runner, you can use the `semaphore runner register` command. This will overwrite the token in the file specified in the configuration. 101 | 102 | ## Running the runner 103 | 104 | Now you can start the runner with the command: 105 | 106 | ``` 107 | semaphore runner start --config /path/to/your/config/file.json 108 | ``` 109 | 110 | Your runner is ready to execute tasks ;) 111 | 112 | ## Runner unregistaration 113 | 114 | You can remove runner using the web interfance. 115 | 116 | 117 | 118 | --- 119 | 120 | Or unregister runner via CLI: 121 | 122 | ``` 123 | semaphore runner unregister --config /path/to/your/config/file.json 124 | ``` 125 | 126 | ## Security 127 | 128 | Data transfer security is ensured by using asymmetric encryption: the server encrypts data using a public key, the runner decrypts it using a private key. 129 | 130 | Public and private keys are generated automatically when the runner registers on the server. 131 | 132 |
133 | Use the HTTPS protocol for communication between the server and the runner, especially if they are not on the same private network. 134 |
135 | -------------------------------------------------------------------------------- /src/administration-guide/security.md: -------------------------------------------------------------------------------- 1 | # 🔐 Security 2 | 3 | ## Introduction 4 | 5 | Security is a top priority in Semaphore UI. Whether you're automating critical infrastructure tasks or managing team access to sensitive systems, Semaphore UI is designed to provide robust, secure operations out of the box. This section outlines how Semaphore handles security and what you should consider when deploying it in production. 6 | 7 | ## Authentication & authorization 8 | 9 | Semaphore supports secure authentication and flexible authorization mechanisms: 10 | 11 | - **Login methods:** 12 | - **Username/password**
Default method using credentials stored in the Semaphore database. Passwords are hashed using a strong algorithm (bcrypt). 13 | 14 | - **LDAP**
Allows integration with enterprise directory services. Supports user/group filtering and secure connections via LDAPS. 15 | 16 | - **OpenID Connect (OIDC)**
Enables single sign-on with identity providers like Google, Azure AD, or Keycloak. Supports custom claims and group mappings. 17 | 18 | - **Two-Factor authentication (2FA)**
TOTP-based 2FA is available and recommended for all users. 19 | 20 | - **Role-based access control**
You can assign different roles to users such as Admin, Maintainer, or Viewer, limiting access based on responsibility. 21 | 22 | - **Session management**
Sessions are protected with secure HTTP cookies. Session expiration and logout mechanisms ensure minimal exposure. 23 | 24 | 25 | ## Secrets & credentials 26 | 27 | Managing secrets securely is a core feature: 28 | 29 | - **Encrypted key store**
Credentials and secret variables are encrypted at rest using AES encryption. 30 | 31 | - **Environment isolation**
Secrets are only passed to jobs at runtime and are not exposed to the container environment directly. 32 | 33 | - **SSH keys and tokens**
Users are responsible for uploading valid SSH keys and tokens. These are encrypted and only used when running tasks. 34 | 35 | 36 | ## Running untrusted code / playbooks 37 | 38 | Semaphore runs user-defined playbooks and commands, which can be risky: 39 | 40 | - **Container isolation**
Tasks are executed in isolated Docker containers. These containers have no access to the host system. 41 | 42 | - **Least privilege**
Containers run with minimal permissions and can be restricted further using Docker flags. 43 | 44 | - **Chroot execution**
Semaphore can execute tasks inside a chroot jail to further isolate the execution environment from the host system. 45 | 46 | - **Task process user**
Tasks can be executed under a dedicated non-root system user (e.g., `semaphore`) to reduce the impact of potential exploits. This is optional and can be configured based on system policies. 47 | 48 | 49 | ## Secure Deployment 50 | 51 | To ensure Semaphore is securely deployed: 52 | 53 | - **Use HTTPS**
54 | Semaphore supports HTTPS both via its **built-in TLS support** and through a **reverse proxy like Nginx**. It is strongly recommended to enable HTTPS in production. 55 | 56 | To enable built-in HTTPS support add following block to **config.json**: 57 | ```json 58 | { 59 | ... 60 | "tls": { 61 | "enabled": true, 62 | "cert_file": "/path/to/cert/example.com.cert", 63 | "key_file": "/path/to/key/example.com.key" 64 | } 65 | ... 66 | } 67 | ``` 68 | 69 | - **Run behind a firewall**
Limit access to the Semaphore UI and database to only trusted IPs. 70 | 71 | - **Database security**
Use strong passwords and restrict database access to Semaphore only. 72 | 73 | ## Updates & patch management 74 | 75 | Security updates are published regularly: 76 | 77 | - **Stay updated**
Always use the latest stable release. 78 | 79 | - **Changelog**
Review changes on GitHub before updating. 80 | 81 | - **Automatic updates**
If using Docker, consider automation pipelines for regular updates. 82 | 83 | 90 | 91 | 98 | 99 | 105 | 106 | 113 | 114 | ## Reporting Vulnerabilities 115 | 116 | Found a vulnerability? Help us keep Semaphore secure: 117 | 118 | - **Responsible disclosure**
Please email us at `security@semaphoreui.com`. 119 | 120 | - **No public exploits**
Do not share vulnerabilities publicly until patched. 121 | 122 | - **Acknowledgments**
Security researchers may be acknowledged in release notes if desired. 123 | 124 | -------------------------------------------------------------------------------- /src/administration-guide/security/apache.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | # Apache config 9 | 10 | Make sure you have enabled following Apache modules: 11 | 12 | ```bash 13 | sudo a2enmod proxy 14 | sudo a2enmod proxy_http 15 | sudo a2enmod proxy_wstunnel 16 | ``` 17 | 18 | Add following virtual host to your Apache configuration: 19 | 20 | ``` 21 | 22 | 23 | ServerName example.com 24 | 25 | ServerAdmin webmaster@localhost 26 | 27 | SSLEngine on 28 | SSLCertificateFile /path/to/example.com.crt 29 | SSLCertificateKeyFile /path/to/example.com.key 30 | 31 | ProxyPreserveHost On 32 | 33 | 34 | ProxyPass http://127.0.0.1:3000/ 35 | ProxyPassReverse http://127.0.0.1:3000/ 36 | 37 | 38 | 39 | RewriteCond %{HTTP:Connection} Upgrade [NC] 40 | RewriteCond %{HTTP:Upgrade} websocket [NC] 41 | 42 | ProxyPass ws://127.0.0.1:3000/api/ws/ 43 | ProxyPassReverse ws://127.0.0.1:3000/api/ws/ 44 | 45 | 46 | 47 | ``` 48 | -------------------------------------------------------------------------------- /src/administration-guide/security/database.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Database security 8 | 9 | ## Data encryption 10 | 11 | Sensitive data is stored in the database, in an encrypted form. You should set the configuration option `access_key_encryption` in configuration file to enable Access Keys encryption. It must be generated by command: 12 | 13 | ```bash 14 | head -c32 /dev/urandom | base64 -------------------------------------------------------------------------------- /src/administration-guide/security/kerberos.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/administration-guide/security/kerberos.md -------------------------------------------------------------------------------- /src/administration-guide/security/network.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Network security 8 | 9 | For security reasons, Semaphore **should not be used** over unencrypted HTTP! 10 | 11 | Why use encrypted connections? See: [Article from Cloudflare](https://www.cloudflare.com/learning/ssl/why-use-https/). 12 | 13 | Options you have: 14 | 15 | * [VPN](#vpn) 16 | * [SSL](#ssl) 17 | 18 | --- 19 | 20 | ## VPN 21 | 22 | You can use a Client-to-Site VPN, that terminates on the Semaphore server, to encrypt & secure the connection. 23 | 24 | ## SSL 25 | 26 | Semaphore supports SSL/TLS starting from v2.12. 27 | 28 | **config.json**: 29 | ```json 30 | { 31 | ... 32 | "tls": { 33 | "enabled": true, 34 | "cert_file": "/path/to/cert/example.com.cert", 35 | "key_file": "/path/to/key/example.com.key" 36 | } 37 | ... 38 | } 39 | ``` 40 | 41 | Or environment varibles (useful for Docker): 42 | 43 | ```bash 44 | export SEMAPHORE_TLS_ENABLED=True 45 | export SEMAPHORE_TLS_CERT_FILE=/path/to/cert/example.com.cert 46 | export SEMAPHORE_TLS_KEY_FILE=/path/to/key/example.com.key 47 | ``` 48 | 49 | Alternatively, you can use a reverse proxy in front of Semaphore to handle secure connections. For example: 50 | 51 | * [NGINX](./nginx.md) 52 | * [Apache](./apache.md) 53 | 54 | 55 | ### Self-signed SSL certificate 56 | 57 | You can generate your own SSL certificate with using `openssl` CLI tool: 58 | 59 | ``` 60 | openssl req -x509 -newkey rsa:4096 \ 61 | -keyout key.pem -out cert.pem \ 62 | -sha256 -days 3650 -nodes \ 63 | -subj "/C=US/ST=California/L=San Francisco/O=CompanyName/OU=DevOps/CN=example.com" 64 | ``` 65 | 66 | ### Let's Encrypt SSL certificate 67 | 68 | You can use [Certbot](https://certbot.eff.org/) to generate and automatically renew a Let's Encrypt SSL certificate. 69 | 70 | Example for Apache: 71 | 72 | ```bash 73 | sudo snap install certbot 74 | sudo certbot --apache -n --agree-tos -d example.com -m mail@example.com 75 | ``` 76 | 77 | ### Others 78 | 79 | If you want to use any other reverse proxy - make sure to also forward websocket connections on the `/api/ws` route! 80 | -------------------------------------------------------------------------------- /src/administration-guide/security/nginx.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Nginx config 8 | 9 | 10 | 11 | Configuration example: 12 | 13 | ```yaml 14 | server { 15 | listen 443 ssl; 16 | server_name example.com; 17 | 18 | # add Strict-Transport-Security to prevent man in the middle attacks 19 | add_header Strict-Transport-Security "max-age=31536000" always; 20 | 21 | # SSL 22 | ssl_certificate /etc/nginx/cert/cert.pem; 23 | ssl_certificate_key /etc/nginx/cert/privkey.pem; 24 | 25 | # Recommendations from 26 | # https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html 27 | ssl_protocols TLSv1.1 TLSv1.2; 28 | ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; 29 | ssl_prefer_server_ciphers on; 30 | ssl_session_cache shared:SSL:10m; 31 | 32 | # required to avoid HTTP 411: see Issue #1486 33 | # (https://github.com/docker/docker/issues/1486) 34 | chunked_transfer_encoding on; 35 | 36 | location / { 37 | proxy_pass http://127.0.0.1:3000/; 38 | proxy_set_header Host $http_host; 39 | proxy_set_header X-Real-IP $remote_addr; 40 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 41 | 42 | proxy_set_header X-Forwarded-Proto $scheme; 43 | 44 | proxy_buffering off; 45 | proxy_request_buffering off; 46 | } 47 | 48 | location /api/ws { 49 | proxy_pass http://127.0.0.1:3000/api/ws; 50 | proxy_http_version 1.1; 51 | proxy_set_header Upgrade $http_upgrade; 52 | proxy_set_header Connection "upgrade"; 53 | proxy_set_header Origin ""; 54 | } 55 | } 56 | ``` -------------------------------------------------------------------------------- /src/administration-guide/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## Renner prints error 404 4 | 5 | ### How to fix 6 | 7 | [Getting 401 error code from Runner](https://github.com/semaphoreui/semaphore/discussions/1873?converting=1) 8 | 9 | --- 10 | 11 | ## Gathering Facts issue for localhost 12 | 13 | The issue can occur on Semaphore UI installed via [Snap](https://snapcraft.io/semaphore) or [Docker](https://hub.docker.com/r/semaphoreui/semaphore). 14 | 15 | ``` 16 | 4:10:16 PM 17 | TASK [Gathering Facts] ********************************************************* 18 | 4:10:17 PM 19 | fatal: [localhost]: FAILED! => changed=false 20 | ``` 21 | 22 | ### Why this happens 23 | 24 | For more information about localhost use in Ansible, read this article [Implicit 'localhost'](https://docs.ansible.com/ansible/latest/inventory/implicit_localhost.html). 25 | 26 | Ansible tries to gather facts locally, but Ansible is located in a limited isolated container which doesn't allow this. 27 | 28 | ### How to fix this 29 | 30 | There are two ways: 31 | 32 | 1. Disable facts gathering: 33 | 34 | ```yaml 35 | - hosts: localhost 36 | gather_facts: False 37 | roles: 38 | - ... 39 | ``` 40 | 41 | 2. Explicitly set the connection type to **ssh**: 42 | ``` 43 | [localhost] 44 | 127.0.0.1 ansible_connection=ssh ansible_ssh_user=your_localhost_user 45 | ``` 46 | --- 47 | ## panic: pq: SSL is not enabled on the server 48 | 49 | This means that your Postgres doesn't work by SSL. 50 | 51 | ### How to fix this 52 | 53 | Add option `sslmode=disable` to the configuration file: 54 | 55 | ```json 56 | "postgres": { 57 | "host": "localhost", 58 | "user": "pastgres", 59 | "pass": "pwd", 60 | "name": "semaphore", 61 | "options": { 62 | "sslmode": "disable" 63 | } 64 | }, 65 | ``` 66 | --- 67 | ## fatal: bad numeric config value '0' for 'GIT_TERMINAL_PROMPT': invalid unit 68 | 69 | This means that you are trying to access a repository over HTTPS that requires authentication. 70 | 71 | ### How to fix this 72 | 73 | * Go to **Key Store** screen. 74 | * Create a new key `Login with password` type. 75 | * Specify your login for GitHub/BitBucket/etc. 76 | * Specify the password. You can't use your account password for GitHub/BitBucket, you should use a Personal Access Token (PAT) instead of it. Read more [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). 77 | * After creating the key, go to the **Repositories** screen, find your repository and specify the key. 78 | 79 | --- 80 | 81 | ## unable to read LDAP response packet: unexpected EOF 82 | 83 | Most likely, you are trying to connect to the LDAP server using an insecure method, although it expects a secure connection (via TLS). 84 | 85 | ### How to fix this 86 | 87 | Enable TLS in your `config.json` file: 88 | 89 | ```json 90 | ... 91 | "ldap_needtls": true 92 | ... 93 | ``` 94 | 95 | --- 96 | 97 | ## LDAP Result Code 49 "Invalid Credentials" 98 | 99 | You have the wrong password or `binddn`. 100 | 101 | ### How to fix this 102 | 103 | Use `ldapwhoami` tool and check if your binddn works: 104 | 105 | ```bash 106 | ldapwhoami\ 107 | -H ldap://ldap.com:389\ 108 | -D "CN=/your/ldap_binddn/value/in/config/file"\ 109 | -x\ 110 | -W 111 | ``` 112 | 113 | It will ask interactively for the password and should return code **0** and echo out the **DN** as specified. 114 | 115 | You also can read the following articles: 116 | * [ldapsearch: Invalid credentials (49)](https://serverfault.com/q/771549/443463) 117 | * [https://github.com/semaphoreui/semaphore/issues/906](https://github.com/semaphoreui/semaphore/issues/906) 118 | 119 | --- 120 | 121 | ## LDAP Result Code 32 "No Such Object" 122 | 123 | Coming soon. 124 | -------------------------------------------------------------------------------- /src/administration-guide/upgrading.md: -------------------------------------------------------------------------------- 1 | # Upgrading 2 | 3 | There are 4 ways for upgrading Semaphore: 4 | 5 | * Snap 6 | * Package manager 7 | * Docker 8 | * Binary 9 | 10 | ### Snap 11 | 12 | Use the following command for upgrading Semaphore to the latest stable version: 13 | 14 | ``` 15 | sudo snap refresh semaphore 16 | ``` 17 | 18 | ### Package manager 19 | 20 | Download a package file from [Releases page](https://github.com/semaphoreui/semaphore/releases). 21 | 22 | `*.deb` for Debian and Ubuntu, `*.rpm` for CentOS and RedHat. 23 | 24 | Install it using the package manager. 25 | 26 | {{#tabs }} 27 | {{#tab name="Debian / Ubuntu (x64)" }} 28 | ``` 29 | wget https://github.com/semaphoreui/semaphore/releases/\ 30 | 31 | download/v2.13.14/semaphore_2.13.14_linux_amd64.deb 32 | 33 | sudo dpkg -i semaphore_2.13.14_linux_amd64.deb 34 | ``` 35 | {{#endtab }} 36 | 37 | {{#tab name="Debian / Ubuntu (ARM64)" }} 38 | ``` 39 | wget https://github.com/semaphoreui/semaphore/releases/\ 40 | 41 | download/v2.13.14/semaphore_2.13.14_linux_arm64.deb 42 | 43 | sudo dpkg -i semaphore_2.13.14_linux_arm64.deb 44 | ``` 45 | {{#endtab }} 46 | 47 | {{#tab name="CentOS (x64)" }} 48 | ``` 49 | wget https://github.com/semaphoreui/semaphore/releases/\ 50 | 51 | download/v2.13.14/semaphore_2.13.14_linux_amd64.rpm 52 | 53 | sudo yum install semaphore_2.13.14_linux_amd64.rpm 54 | ``` 55 | {{#endtab }} 56 | 57 | {{#tab name="CentOS (ARM64)" }} 58 | ``` 59 | wget https://github.com/semaphoreui/semaphore/releases/\ 60 | 61 | download/v2.13.14/semaphore_2.13.14_linux_arm64.rpm 62 | 63 | sudo yum install semaphore_2.13.14_linux_arm64.rpm 64 | ``` 65 | {{#endtab }} 66 | {{#endtabs }} 67 | 68 | ### Docker 69 | 70 |
71 | Coming soon 72 |
73 | 74 | ### Binary 75 | 76 | Download a `*.tar.gz` for your platform from [Releases page](https://github.com/semaphoreui/semaphore/releases). Unpack the binary to the directory where your old Semaphore binary is located. 77 | 78 | {{#tabs }} 79 | {{#tab name="Linux (x64)" }} 80 | ``` 81 | wget https://github.com/semaphoreui/semaphore/releases/\ 82 | 83 | download/v2.13.14/semaphore_2.13.14_linux_amd64.tar.gz 84 | 85 | tar xf semaphore_2.13.14_linux_amd64.tar.gz 86 | ``` 87 | {{#endtab }} 88 | 89 | {{#tab name="Linux (ARM64)" }} 90 | ``` 91 | wget https://github.com/semaphoreui/semaphore/releases/\ 92 | 93 | download/v2.13.14/semaphore_2.13.14_linux_arm64.tar.gz 94 | 95 | tar xf semaphore_2.13.14_linux_arm64.tar.gz 96 | ``` 97 | {{#endtab }} 98 | 99 | {{#tab name="Windows (x64)" }} 100 | ``` 101 | Invoke-WebRequest ` 102 | -Uri ("https://github.com/semaphoreui/semaphore/releases/" + 103 | "download/v2.13.14/semaphore_2.13.14_windows_amd64.zip") ` 104 | -OutFile semaphore.zip 105 | 106 | Expand-Archive -Path semaphore.zip -DestinationPath ./ 107 | ``` 108 | {{#endtab }} 109 | {{#endtabs }} 110 | 111 | -------------------------------------------------------------------------------- /src/faq/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## 1. Renner prints error 404 4 | 5 | ### How to fix 6 | 7 | [Getting 401 error code from Runner](https://github.com/semaphoreui/semaphore/discussions/1873?converting=1) 8 | 9 | --- 10 | 11 | ## 2. Gathering Facts issue for localhost 12 | 13 | The issue can occur on Semaphore UI installed via [Snap](https://snapcraft.io/semaphore) or [Docker](https://hub.docker.com/r/semaphoreui/semaphore). 14 | 15 | ``` 16 | 4:10:16 PM 17 | TASK [Gathering Facts] ********************************************************* 18 | 4:10:17 PM 19 | fatal: [localhost]: FAILED! => changed=false 20 | ``` 21 | 22 | ### Why this happens 23 | 24 | For more information about localhost use in Ansible, read this article [Implicit 'localhost'](https://docs.ansible.com/ansible/latest/inventory/implicit_localhost.html). 25 | 26 | Ansible tries to gather facts locally, but Ansible is located in a limited isolated container which doesn't allow this. 27 | 28 | ### How to fix this 29 | 30 | There are two ways: 31 | 32 | 1. Disable facts gathering: 33 | 34 | ```yaml 35 | - hosts: localhost 36 | gather_facts: False 37 | roles: 38 | - ... 39 | ``` 40 | 41 | 2. Explicitly set the connection type to **ssh**: 42 | ``` 43 | [localhost] 44 | 127.0.0.1 ansible_connection=ssh ansible_ssh_user=your_localhost_user 45 | ``` 46 | --- 47 | ## 4. panic: pq: SSL is not enabled on the server 48 | 49 | This means that your Postgres doesn't work by SSL. 50 | 51 | ### How to fix this 52 | 53 | Add option `sslmode=disable` to the configuration file: 54 | 55 | ```json 56 | "postgres": { 57 | "host": "localhost", 58 | "user": "pastgres", 59 | "pass": "pwd", 60 | "name": "semaphore", 61 | "options": { 62 | "sslmode": "disable" 63 | } 64 | }, 65 | ``` 66 | 67 | 68 | --- 69 | 70 | 71 | ## 5. fatal: bad numeric config value '0' for 'GIT_TERMINAL_PROMPT': invalid unit 72 | 73 | This means that you are trying to access a repository over HTTPS that requires authentication. 74 | 75 | ### How to fix this 76 | 77 | * Go to **Key Store** screen. 78 | * Create a new key `Login with password` type. 79 | * Specify your login for GitHub/BitBucket/etc. 80 | * Specify the password. You can't use your account password for GitHub/BitBucket, you should use a Personal Access Token (PAT) instead of it. Read more [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). 81 | * After creating the key, go to the **Repositories** screen, find your repository and specify the key. 82 | 83 | 84 | --- 85 | 86 | 87 | 88 | ## 6. unable to read LDAP response packet: unexpected EOF 89 | 90 | Most likely, you are trying to connect to the LDAP server using an insecure method, although it expects a secure connection (via TLS). 91 | 92 | ### How to fix this 93 | 94 | Enable TLS in your `config.json` file: 95 | 96 | ```json 97 | ... 98 | "ldap_needtls": true 99 | ... 100 | ``` 101 | 102 | --- 103 | 104 | ## 7. LDAP Result Code 49 "Invalid Credentials" 105 | 106 | You have the wrong password or `binddn`. 107 | 108 | ### How to fix this 109 | 110 | Use `ldapwhoami` tool and check if your binddn works: 111 | 112 | ```bash 113 | ldapwhoami\ 114 | -H ldap://ldap.com:389\ 115 | -D "CN=/your/ldap_binddn/value/in/config/file"\ 116 | -x\ 117 | -W 118 | ``` 119 | 120 | It will ask interactively for the password and should return code **0** and echo out the **DN** as specified. 121 | 122 | You also can read the following articles: 123 | * [ldapsearch: Invalid credentials (49)](https://serverfault.com/q/771549/443463) 124 | * [https://github.com/semaphoreui/semaphore/issues/906](https://github.com/semaphoreui/semaphore/issues/906) 125 | 126 | --- 127 | 128 | ## 8. LDAP Result Code 32 "No Such Object" 129 | 130 | Coming soon. 131 | -------------------------------------------------------------------------------- /src/user-guide/admin/README.md: -------------------------------------------------------------------------------- 1 | # Admin 2 | -------------------------------------------------------------------------------- /src/user-guide/admin/runners.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/user-guide/admin/runners.md -------------------------------------------------------------------------------- /src/user-guide/admin/subscription.md: -------------------------------------------------------------------------------- 1 | # Subscription 🅿 2 | -------------------------------------------------------------------------------- /src/user-guide/admin/tasks.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/user-guide/admin/tasks.md -------------------------------------------------------------------------------- /src/user-guide/admin/users.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/user-guide/admin/users.md -------------------------------------------------------------------------------- /src/user-guide/environment.md: -------------------------------------------------------------------------------- 1 | # Variable Groups 2 | 3 | The Variable Groups section of Semaphore is a place to store additional variables for an inventory and must be stored in JSON format. 4 | 5 | All task templates require an variable group to be defined even if it is empty. 6 | 7 | ## Create an variable group 8 | 1. Click on the Variable Group tab. 9 | 2. Click on the New Variable Group button. 10 | 3. Name the Variable Group and type or paste in valid JSON variables. If you just need an empty Variable Group type in ```{}```. 11 | 12 | ## Updating an variable group 13 | 1. Click on the Variable Groups tab. 14 | 2. Click the pencil icon. 15 | 3. Make changes and click save. 16 | 17 | ## Deleting the variable group 18 | Before you remove an variable proup, you must remove all resources tied to it. 19 | If you are not sure which resources are being used in an variable group, follow steps 1 and 2 below. It will show you which resources are being used, with links to those resources. 20 | 21 | 1. Click on the Variable Group. 22 | 2. Click the trash can icon next to the Variable Group. 23 | 3. Click Yes if you are sure you want to remove the variable group. 24 | 25 | ## Using Variable Groups - Terraform 26 | When you want utilize a stored variable group variable or secret in your terraform template you must prefix the name with `TF_VAR_` for the terraform script to use it. 27 | 28 | **Example** 29 | Passing Hetzner Cloud API key to OpenTofu/Terraform playbook. 30 | 31 | 1. Click on Variable Group 32 | 2. Click `New Group` 33 | 3. Click on `Secrets` tab 34 | 4. Add `TF_VAR_hcloud_token` and add you `secret` in the hidden field 35 | 5. Click Save 36 | 37 | We will call our secret `TF_VAR_hcloud_token` as `var.hcloud_token` in 38 | hetzner.tf 39 | ``` 40 | terraform { 41 | required_providers { 42 | hcloud = { 43 | source = "hetznercloud/hcloud" 44 | version = "~> 1.45" 45 | } 46 | } 47 | } 48 | 49 | # Declare the variable 50 | variable "hcloud_token" { 51 | type = string 52 | description = "Hetzner Cloud API token" 53 | sensitive = true # This prevents the token from being displayed in logs 54 | } 55 | 56 | provider "hcloud" { 57 | token = var.hcloud_token 58 | } 59 | 60 | # Create a new server running debian 61 | resource "hcloud_server" "webserver" { 62 | name = "webserver" 63 | image = "ubuntu-24.04" 64 | server_type = "cpx11" 65 | location = "ash" 66 | ssh_keys = [ "mysshkey" ] 67 | public_net { 68 | ipv4_enabled = true 69 | ipv6_enabled = true 70 | } 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /src/user-guide/integrations.md: -------------------------------------------------------------------------------- 1 | # Integrations 2 | 3 | Integrations allow establishing interaction between Semaphore and external services, such as GitHub and GitLab. 4 | 5 | ![](<../.gitbook/assets/integrations_1.jpg>) 6 | 7 | Using integration, you can trigger a specific template by calling a special endpoint (alias), for which you can configure one of the following authentication methods: 8 | * GitHub Webhooks 9 | * Token 10 | * HMAC 11 | * No authentication 12 | 13 | The alias represents a URL in the following format: `/api/integrations/`. Supports `GET` and `POST` requests. 14 | 15 | ## Matchers 16 | 17 | With matchers, you can define parameters of the incoming request. When these parameters match, the template will be invoked. 18 | 19 | ## Value Extractors 20 | 21 | With an extractor, you can extract the necessary data from the incoming request and pass it to the task as environment variables. For the extracted variables to be passed to the 22 | task, you must create an environment with the corresponding keys. Ensure that the environment keys match the variables defined in the extractor, as this allows the task to receive 23 | and use the correct environment variables. 24 | -------------------------------------------------------------------------------- /src/user-guide/inventory.md: -------------------------------------------------------------------------------- 1 | # Inventory 2 | 3 | An Inventory is a file that contains a list of hosts Ansible will run plays against. 4 | An Inventory also stores variables that can be used by playbooks. An Inventory can be stored in YAML, JSON, or TOML. 5 | More information about Inventories can be found in the [Ansible Documentation.](https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html) 6 | 7 | Semaphore UI can either read an Inventory from a file on the server that the Semaphore user has read access to, or a static Inventory that is edited via the web GUI. 8 | Each Inventory also has at least one credential tied to it. 9 | The user credential is required, and is what Ansible uses to log into hosts for that Inventory. Sudo credentials are used for escalating privileges on that host. 10 | It is required to have a user credential that is either a username with a login, or SSH configured in the Key Store to create an Inventory. 11 | Information about credentials can be found in the [Key Store](key-store.md) section of this site. 12 | 13 | ## Creating an Inventory 14 | 1. Click on the Key Store tab and confirm you have a key that is a login_password or ssh type 15 | 2. Click on the Inventory tab and click New Inventory 16 | 4. Name the Inventory and select the correct user credential from the dropdown. Select the correct sudo credential, if needed 17 | 5. Select the Inventory type 18 | * If you select file, use the absolute path to the file. If this file is located in your git repo, then use relative path. Ex. `inventory/linux-hosts.yaml` 19 | * If you select static, paste in or type your Inventory into the form 20 | 6. Click Create. 21 | 22 | ## Updating an Inventory 23 | 1. Click on the Inventory tab 24 | 2. Click the Pencil Icon next to the Inventory you want to edit 25 | 3. Make your changes 26 | 4. Click Save 27 | 28 | ## Deleting an Inventory 29 | Before you remove an Inventory, you must remove all resources tied to it. 30 | If you are not sure which resources are being used in an environment, follow steps 1 and 2 below. It will show you which resources are being used, with links to those resources. 31 | 32 | 1. Click on the Inventory tab 33 | 2. Click the trash can icon next to the Inventory 34 | 3. Click Yes if you are sure you want to remove the Inventory 35 | -------------------------------------------------------------------------------- /src/user-guide/inventory/kerberos.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | # Kerberos authentication 9 | 10 | Semaphore supports Kerberos authentication when running playbooks against **Windows hosts via WinRM**. 11 | 12 | ## Inventory configuration 13 | 14 | ```ini 15 | [windows] 16 | hostname 17 | 18 | [windows:vars] 19 | ansible_port=5985 20 | ansible_connection=winrm 21 | ansible_winrm_server_cert_validation=ignore 22 | ansible_winrm_transport=ntlm 23 | ansible_winrm_kinit_mode=managed 24 | ansible_winrm_scheme=http 25 | ``` 26 | 27 | Also make sure: 28 | 29 | * A username and password are provided (Semaphore credentials) 30 | * The user format is `domain\\username` (e.g., `CORP\\admin`) if needed 31 | 32 | The key setting is: 33 | 34 | ```ini 35 | ansible_winrm_kinit_mode=managed 36 | ``` 37 | 38 | This tells Ansible to **automatically acquire a Kerberos ticket** using the provided username/password without requiring you to manually run kinit. 39 | 40 | 41 | ## Example Playbook 42 | 43 | ```yaml 44 | - hosts: all 45 | gather_facts: false 46 | 47 | tasks: 48 | - win_ping: 49 | ``` 50 | 51 | This verifies basic connectivity using WinRM + Kerberos. 52 | 53 | 54 | ## Semaphore UI host requirements 55 | 56 | On the Semaphore host, install the following packages: 57 | 58 | ```bash 59 | sudo apt install libkrb5-dev krb5-user 60 | ``` 61 | 62 | Then edit `/etc/krb5.conf` and set your default realm (domain name): 63 | 64 | ```ini 65 | [libdefaults] 66 | default_realm = YOUR.DOMAIN.NAME 67 | ``` 68 | 69 | This must match your Active Directory domain. 70 | 71 | ## Notes 72 | 73 | * You do not need to run kinit manually — Ansible handles ticket acquisition when `ansible_winrm_kinit_mode=managed` is set. 74 | 75 | * Works with the default NTLM transport (no SSL needed if using HTTP and `cert_validation=ignore`). -------------------------------------------------------------------------------- /src/user-guide/key-store.md: -------------------------------------------------------------------------------- 1 | # Key Store 2 | 3 | The Key Store in Semaphore is used to store credentials for accessing remote Repositories, accessing remote hosts, sudo credentials, and Ansible vault passwords. 4 | 5 | It is helpful to have configured all required access keys before setting up other resources like Inventories, Repositories, and tasks templates so you do not have to edit them later. 6 | 7 | ## Types 8 | 9 | ### 1. SSH 10 | SSH Keys are used to access remote servers as well as remote Repositories. 11 | 12 | If you need assistance quickly generating a key and placing it on your host, [here is a quick guide.](https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-20-04) 13 | 14 | For Git Repositories that use SSH authentication, the Git Repository you are trying to clone from needs to have your public key associated to the private key. 15 | 16 | Below are links to the docs for some common Git Repositories: 17 | * [GitHub](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account) 18 | * [GitLab](https://docs.gitlab.com/ee/user/ssh.html) 19 | * [Bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/set-up-an-ssh-key/) 20 | 21 | ### 2. Login With Password 22 | Login With Password is a username and password/access token combination that can be used to do the following: 23 | * Authenticate to remote hosts (although this is less secure than using SSH keys) 24 | * Sudo credentials on remote hosts 25 | * Authenticate to remote Git Repositories over HTTPS (although SSH is more secure) 26 | * Unlock Ansible vaults 27 | 28 |
29 | This type of secret can be used as Personal Access Token (PAT) or secret string. Simply leave the Login field empty. 30 |
31 | 32 | ### 3. None 33 | This is used as a filler for Repos that do not require authentication, like an Open-Source Repository on GitLab. 34 | -------------------------------------------------------------------------------- /src/user-guide/key-store/gitlab.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # GitLab 8 | 9 | ## GitLab repository access token 10 | 11 | -------------------------------------------------------------------------------- /src/user-guide/netbox-dynamic-inventory.md: -------------------------------------------------------------------------------- 1 | # Netbox Dynamic Inventory Integration with Semaphore 2 | 3 | ![Ansible Badge](https://img.shields.io/badge/ansible-%23000.svg?style=for-the-badge&logo=ansible&logoColor=white) 4 | ![Netbox Badge](https://img.shields.io/badge/Netbox-%23F00.svg?style=for-the-badge&logo=netbox&logoColor=white) 5 | ![Semaphore Badge](https://img.shields.io/badge/Semaphore-%23187EBB.svg?style=for-the-badge&logo=semaphore&logoColor=white) 6 | 7 | ## 🛠 Key Features 8 | 9 | This repository demonstrates the use of the `netbox.netbox.nb_inventory` plugin to create a dynamic inventory in Semaphore. It enables automatic synchronization of data from Netbox, simplifying the management of your infrastructure and the execution of Ansible playbooks. 10 | 11 | ## 🔧 Setup 12 | 13 | ### Requirements 14 | 15 | - Access to Semaphore 16 | - Access to Netbox with configured API 17 | 18 | ### 🔑 Netbox Setup 19 | 20 | Ensure your Netbox is configured and accessible for API interaction. Obtain an API token which will be used to authenticate requests. 21 | 22 | ### 📡 Configuration in Semaphore 23 | 24 | 1. In Semaphore, go to the inventory section. 25 | 2. Create a new inventory. 26 | 3. Enter the following settings for the plugin configuration: 27 | 28 | ```yaml 29 | plugin: netbox.netbox.nb_inventory 30 | api_endpoint: http://your_netbox_url_here 31 | token: YOUR_NETBOX_API_TOKEN 32 | validate_certs: False 33 | config_context: False 34 | ``` 35 | 36 | Replace `http://your_netbox_url_here` and `YOUR_NETBOX_API_TOKEN` with the actual data from your Netbox. 37 | 38 | ## 🚀 Usage 39 | 40 | Once configured, you can run Ansible playbooks in Semaphore using the dynamic inventory which automatically updates host data from your Netbox. 41 | 42 | ## 📚 Further Documentation 43 | 44 | Learn more about the `netbox.netbox.nb_inventory` plugin and its capabilities in the [official Ansible documentation](https://docs.ansible.com/ansible/latest/collections/netbox/netbox/nb_inventory_inventory.html). 45 | -------------------------------------------------------------------------------- /src/user-guide/projects.md: -------------------------------------------------------------------------------- 1 | # Projects 2 | 3 | A project is a place to separate management activity. 4 | 5 | All Semaphore activities occur within the context of a project. 6 | 7 | Projects are independent from one another, so you can use them to organize unrelated systems within a single Semaphore installation. 8 | 9 | This can be useful for managing different teams, infrastructures, environments or applications. 10 | 11 | ![](../.gitbook/assets/project\_new\_ipad.png) 12 | 13 | -------------------------------------------------------------------------------- /src/user-guide/projects/activity.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Activity 7 | -------------------------------------------------------------------------------- /src/user-guide/projects/history.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # History 7 | -------------------------------------------------------------------------------- /src/user-guide/projects/runners.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Runners (Pro) 7 | 8 | -------------------------------------------------------------------------------- /src/user-guide/projects/settings.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Settings 7 | -------------------------------------------------------------------------------- /src/user-guide/repositories.md: -------------------------------------------------------------------------------- 1 | # Repositories 2 | 3 | A Repository is a place to store and manage Ansible content like playbooks and roles. 4 | 5 | ![](<../.gitbook/assets/repository.webp>) 6 | 7 | Semaphore understands Repositories that are: 8 | * a local file system (`/path/to/the/repo`) 9 | * a local Git repository (`file://`) 10 | * a remote Git Repository that is accessed over HTTPS (`https://`), SSH(`ssh://`) 11 | * `git://` protocol supported, but it is not recommended for security reasons. 12 | 13 | All Task Templates require a Repository in order to run. 14 | 15 | ## Authentication 16 | If you are using a remote Repository that requires authentication, you will need to configure a key in the **Key Store** section of Semaphore. 17 | 18 | For remote Repositories that use SSH, you will need to use your SSH key in the **Key Store**. 19 | 20 | For Remote Repositories that do not have authentication, you can create a Key with the type of `None`. 21 | 22 | ## Creating a New Repository 23 | 1. Make sure you have configured the key for the Repository you are about to add in the key store section. 24 | 25 | 2. Go to the Repositories section of Semaphore, click the **New Repository** button in the upper right hand corner. 26 | 27 | 3. Configure the Repository: 28 | * Name Repository 29 | * Add the URL. The URL must start with the following: 30 | * `/path/to/the/repo` for a local folder on the file system 31 | * `https://` for a remote Git Repository accessed over HTTPS 32 | * `ssh://` for a remote Git Repository accessed over SSH 33 | * `file://` for a local Git Repository 34 | * `git://` for a remote Git Repository accessed over Git protocol 35 | * Set the branch of the Repository, if you are not sure what it should be, it is probably master or main 36 | * Select the **Access Key** you configured prior to setting up this Repository. 37 | 38 | 4. Click Save once everything is configured. 39 | 40 | ## Editing an Existing Repository 41 | 1. Go to the Repositories section of Semaphore. 42 | 43 | 2. Click on the pencil icon next to the Repository you wish to change, then you will be presented with the Repository configuration. 44 | 45 | ## Deleting a Repository 46 | Make sure the Repository that is about to be delete is not in use by any Task Templates. 47 | A Repository cannot be deleted if it is used in any Task Templates: 48 | 1. Go to the Repositories section of Semaphore. 49 | 50 | 2. Click on the trash can icon on of the Repository you wish to delete. 51 | 52 | 3. Click Yes on the confirmation pop-up if you are sure you want this Repository to be deleted. 53 | 54 | ## Requirements 55 | Upon project initialization Semaphore searches for and installs Ansible roles and collections from requirements.yml in the following locations and order. 56 | 57 | ### Roles 58 | 59 | * /roles/requirements.yml 60 | * /requirements.yml 61 | * /roles/requirements.yml 62 | * /requirements.yml 63 | 64 | ### Collection 65 | 66 | * /collections/requirements.yml 67 | * /requirements.yml 68 | * /collections/requirements.yml 69 | * /requirements.yml 70 | 71 | ### Processing Logic 72 | 73 | * Each file is processed independently 74 | * If a file exists, it will be processed according to its type (role or collection) 75 | * If any file processing results in an error, the installation process stops and returns the error 76 | * The same requirements.yml file in the root directories (**/requirements.yml** and **/requirements.yml**) is processed twice - once for roles and once for collections 77 | 78 | Semaphore will attempt to process all these locations regardless of whether previous locations were found or successfully processed, except in the case of errors. 79 | -------------------------------------------------------------------------------- /src/user-guide/repositories/bitbucket_access_token.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Bitbucket Access Token 7 | 8 | You can use a Bitbucket Access Token in Semaphore to access repositories from Bitbucket. 9 | 10 | First, you need to create an Access Token for your Bitbucket repository with read access permissions. 11 | 12 | ![](<../../.gitbook/assets/bitbucket_access_token_1.webp>) 13 | 14 | 15 | After creation, you will see the access token. Copy it to your clipboard as it will be required for creating an **Access Key** in Semaphore. 16 | 17 | ![](<../../.gitbook/assets/bitbucket_access_token_2.webp>) 18 | 19 | 1. Go to to the **Key Store** section in Semaphore and click the **New Key** button. 20 | 21 | 2. Choose `Login with password` as the type of key. 22 | 23 | 3. Enter `x-token-auth` as **Login** and paste the previously copied key into the **Password** field. Save the key.

![](<../../.gitbook/assets/bitbucket_access_token_3.webp>) 24 | 25 | 4. Go to the **Repositories** section and click the **New Repository** button. 26 | 27 | 5. Enter HTTPS URL of the repository (`https://bitbucket.org/path/to/repo`), enter correct branch and select previously created **Access Key**.

![](<../../.gitbook/assets/bitbucket_access_token_4.webp>) -------------------------------------------------------------------------------- /src/user-guide/schedules.md: -------------------------------------------------------------------------------- 1 | # Schedules 2 | 3 | The schedule function in Semaphore allows to automate the execution of templates (e.g. playbook runs) at predefined intervals. This feature allows to implement routine automation tasks, such as regular backups, compliance checks, system updates, and more. 4 | 5 | Make sure to restart the Semaphore service after making changes for them to take effect. 6 | 7 | ## Setup and configuration 8 | 9 | ## Timezone configuration 10 | 11 | By default, the schedule feature operates in the UTC timezone. However, this can be customized to match your local timezone or specific requirements. 12 | 13 | You can change the timezone by updating the configuration file or setting an environment variable: 14 | 15 | 1. **Using the configuration file**: 16 | Add or update the `timezone` field in your Semaphore configuration file: 17 | ```json 18 | { 19 | "schedule": { 20 | "timezone": "America/New_York" 21 | } 22 | } 23 | ``` 24 | 25 | 2. **Using an environment variable**: 26 | Set the `SEMAPHORE_SCHEDULE_TIMEZONE` environment variable: 27 | ```bash 28 | export SEMAPHORE_SCHEDULE_TIMEZONE="America/New_York" 29 | ``` 30 | 31 | For a list of valid timezone values, refer to the [IANA Time Zone Database](https://www.iana.org/time-zones). 32 | 33 | ### Accessing the schedule feature 34 | 35 | 1. Log in to your Ansible Semaphore web interface 36 | 2. Navigate to the "Schedule" tab in the main navigation menu 37 | 3. Click the "New Schedule" button in the top right corner to create a new schedule 38 | 39 | ![](<../.gitbook/assets/schedule01.png>) 40 | 41 | ### Creating a new schedule 42 | 43 | When creating a new schedule, you'll need to configure the following options: 44 | 45 | | Field | Description | 46 | |-------|-------------| 47 | | Name | A descriptive name for the scheduled task | 48 | | Template | The specific Task Template to execute | 49 | | Timing | Either in cron format for more fexibility or using the built-in options for common intervals | 50 | 51 | ![](<../.gitbook/assets/schedule02.png>) ![](<../.gitbook/assets/schedule03.png>) 52 | 53 | ### Cron format syntax 54 | 55 | The schedule uses standard cron syntax with five fields: 56 | 57 | ``` 58 | ┌─────── minute (0-59) 59 | │ ┌────── hour (0-23) 60 | │ │ ┌───── day of month (1-31) 61 | │ │ │ ┌───── month (1-12) 62 | │ │ │ │ ┌───── day of week (0-6) (Sunday=0) 63 | │ │ │ │ │ 64 | │ │ │ │ │ 65 | * * * * * 66 | ``` 67 | 68 | Examples: 69 | - `*/15 * * * *` - Run every 15 minutes 70 | - `0 2 * * *` - Run at 2:00 AM every day 71 | - `0 0 * * 0` - Run at midnight on Sundays 72 | - `0 9 1 * *` - Run at 9:00 AM on the first day of every month 73 | 74 | Very helpful cron expression generator: [https://crontab.guru/](https://crontab.guru/) 75 | 76 | ## Use cases 77 | 78 | ### System maintenance 79 | 80 | ```yaml 81 | # Example playbook for system updates 82 | --- 83 | - hosts: all 84 | become: yes 85 | tasks: 86 | - name: Update apt cache 87 | apt: 88 | update_cache: yes 89 | 90 | - name: Upgrade all packages 91 | apt: 92 | upgrade: yes 93 | 94 | - name: Remove dependencies that are no longer required 95 | apt: 96 | autoremove: yes 97 | ``` 98 | 99 | Schedule this playbook to run weekly during off-hours to ensure systems stay up-to-date. 100 | 101 | ### Backup operations 102 | 103 | Create schedules for database backups with different frequencies: 104 | - Daily backups that retain for one week 105 | - Weekly backups that retain for one month 106 | - Monthly backups that retain for one year 107 | 108 | ### Compliance checks 109 | 110 | Schedule regular compliance scans to ensure systems meet security requirements: 111 | 112 | ```yaml 113 | # Example compliance check playbook 114 | --- 115 | - hosts: all 116 | tasks: 117 | - name: Run compliance checks 118 | script: /path/to/compliance_script.sh 119 | 120 | - name: Collect compliance reports 121 | fetch: 122 | src: /var/log/compliance-report.log 123 | dest: reports/{{ inventory_hostname }}/ 124 | flat: yes 125 | ``` 126 | 127 | ### Environment provisioning and cleanup 128 | 129 | For development or testing environments. Schedule cloud environment creation in the morning and teardown in the evening to optimize costs. 130 | 131 | ## Best practices 132 | 133 | * Use descriptive names for schedules that indicate both function and timing (e.g. "Weekly-Backup-Sunday-2AM") 134 | * Avoid scheduling too many resource-intensive tasks concurrently 135 | * Consider the effect of long-running scheduled tasks on other schedules 136 | * Test schedules with short intervals before setting up production schedules with longer intervals 137 | * Document the purpose and expected outcomes of scheduled tasks 138 | -------------------------------------------------------------------------------- /src/user-guide/task-templates/README.md: -------------------------------------------------------------------------------- 1 | # Task Templates 2 | 3 | Templates define how to run Semaphore tasks. Currently the following task types are supported: 4 | 5 | * Playbook repository 6 | * Playbook filename 7 | * Inventory 8 | * Environment 9 | * Vault password file 10 | * Extra CLI arguments 11 | * and much more 12 | 13 | ![](../../.gitbook/assets/template\_new\_ipad.png) 14 | 15 | The task template can be one of the following types: 16 | 17 | * [Task](#task) 18 | * [Build](#build) 19 | * [Deploy](#deploy) 20 | 21 | ### Task 22 | 23 | Just runs specified playbooks with specified parameters. 24 | 25 | ### Build 26 | 27 | This type of template should be used to create [artifacts](https://en.wikipedia.org/wiki/Artifact\_\(software\_development\)). The start version of the artifact can be specified in a template parameter. Each run increments the artifact version. 28 | 29 | ![](<../../.gitbook/assets/template\_new\_build\_ipad (1).png>) 30 | 31 | Semaphore doesn't support artifacts out-of-box, it only provides task versioning. You should implement the artifact creation yourself. Read the article [CI/CD](../../administration-guide/cicd.md) to know how to do this. 32 | 33 | ### Deploy 34 | 35 | This type of template should be used to deploy artifacts to the destination servers. Each `deploy` template is associated with a `build` template. 36 | 37 | ![](../../.gitbook/assets/template\_new\_deploy\_ipad.png) 38 | 39 | This allows you to deploy a specific version of the artifact to the servers. 40 | 41 | ### Schedule 42 | 43 | You can set up task scheduling by specifying a cron schedule in the template settings. Cron expression format you can find in [documentation](https://pkg.go.dev/github.com/robfig/cron/v3#hdr-CRON\_Expression\_Format). 44 | 45 | ![](../../.gitbook/assets/template\_schedule.png) 46 | 47 | #### Run a task when a new commit is added to the repository 48 | 49 | You can use cron to periodically check for new commits in the repository and trigger a task upon their arrival. 50 | 51 | For example you have source code of the app in the git repository. You can add it to **Repositories** and trigger the Build task for new commits. 52 | 53 | ![](../../.gitbook/assets/template\_schedule\_commit.png) 54 | -------------------------------------------------------------------------------- /src/user-guide/task-templates/apps/ansible.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Ansible 7 | 8 | Using Semaphore UI you can run Ansible playbooks. To do this, you need to create an **Ansible Playbook** Template. 9 | 10 | 1. Go go **Task Templates** section, click on **New Template** and then **Ansible Playbook**. 11 | 12 | ![](<../../../.gitbook/assets/ansible_1.png>) 13 | 14 | 2. Set up the template. 15 | 16 | The template allows you to specify the following parameters: 17 | 18 | * Repository 19 | * Path to playbook file 20 | * Inventory 21 | * Variable Groups 22 | * Vaults 23 | * and much more 24 | 25 | ![](<../../../.gitbook/assets/ansible_2.png>) 26 | 27 | An ansible-playbook template can be one of the following types: 28 | 29 | * [Task](#task) 30 | * [Build](#build) 31 | * [Deploy](#deploy) 32 | 33 | ### Task 34 | 35 | Just runs specified playbooks with specified parameters. 36 | 37 | ### Build 38 | 39 | This type of template should be used to create [artifacts](https://en.wikipedia.org/wiki/Artifact\_\(software\_development\)). The start version of the artifact can be specified in a template parameter. Each run increments the artifact version. 40 | 41 | ![](<../../../.gitbook/assets/template\_new\_build\_ipad (1).png>) 42 | 43 | Semaphore doesn't support artifacts out-of-box, it only provides task versioning. You should implement the artifact creation yourself. Read the article [CI/CD](../../administration-guide/cicd.md) to know how to do this. 44 | 45 | ### Deploy 46 | 47 | This type of template should be used to deploy artifacts to the destination servers. Each `deploy` template is associated with a `build` template. 48 | 49 | ![](../../../.gitbook/assets/template\_new\_deploy\_ipad.png) 50 | 51 | This allows you to deploy a specific version of the artifact to the servers. 52 | 53 | ### Schedule 54 | 55 | You can set up task scheduling by specifying a cron schedule in the template settings. Cron expression format you can find in [documentation](https://pkg.go.dev/github.com/robfig/cron/v3#hdr-CRON\_Expression\_Format). 56 | 57 | ![](../../../.gitbook/assets/template\_schedule.png) 58 | 59 | #### Run a task when a new commit is added to the repository 60 | 61 | You can use cron to periodically check for new commits in the repository and trigger a task upon their arrival. 62 | 63 | For example you have source code of the app in the git repository. You can add it to **Repositories** and trigger the Build task for new commits. 64 | 65 | ![](../../../.gitbook/assets/template\_schedule\_commit.png) 66 | -------------------------------------------------------------------------------- /src/user-guide/task-templates/apps/bash.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Shell/Bash scripts 7 | 8 | Using Semaphore UI you can run Bash scripts. To do this, you need to create a **Bash Script Template**. 9 | 10 | 1. Go go Task Templates section and click the **New Template** button. 11 | 12 | ![](<../../../.gitbook/assets/bash_1.png>) 13 | 14 | 2. Set up the template and click the **Create** button. 15 | 16 | ![](<../../../.gitbook/assets/bash_2.png>) 17 | 18 | 3. You can now run your Bash script. -------------------------------------------------------------------------------- /src/user-guide/task-templates/apps/powershell.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # PowerShell 7 | -------------------------------------------------------------------------------- /src/user-guide/task-templates/apps/python.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Python 7 | -------------------------------------------------------------------------------- /src/user-guide/task-templates/apps/terraform.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Terraform/OpenTofu 7 | 8 | Using Semaphore UI you can run Terraform code. To do this, you need to create a **Terraform Code Template**. 9 | 10 | 1. Go go Task Templates section and click the **New Template** button. 11 | 12 | ![](<../../../.gitbook/assets/terraform_1.webp>) 13 | 14 | 2. Set up the template and click the **Create** button. 15 | 16 | ![](<../../../.gitbook/assets/terraform_2.webp>) 17 | 18 | 3. You can now run your Terraform code. -------------------------------------------------------------------------------- /src/user-guide/task-templates/apps/terraform/states.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # HTTP Backend (Pro) 8 | 9 | The Semaphore UI HTTP backend for Terraform securely stores and manages Terraform state files directly within Semaphore. Available in the Pro plan, it offers several key advantages. 10 | 11 | ## Features 12 | 13 | - **Secure State Storage**: State files are stored securely within Semaphore. 14 | - **State Locking**: Prevents concurrent modifications to the same state file. 15 | - **Version History**: Track changes to your infrastructure state over time. 16 | - **UI Integration**: Manage state files directly through the Semaphore interface. 17 | 18 | ## Configuration 19 | 20 | To use the Semaphore UI HTTP backend, add the following configuration to your Terraform configuration: 21 | 22 | ```hcl 23 | terraform { 24 | backend "http" { 25 | address = "https:///api/terraform/state/" 26 | lock_address = "https:///api/terraform/state//lock" 27 | unlock_address = "https:///api/terraform/state//lock" 28 | username = "semaphore" 29 | password = "" 30 | } 31 | } 32 | ``` 33 | 34 | 35 | 36 | Replace the following placeholders: 37 | - ``: Your Semaphore instance URL 38 | - ``: Your Semaphore project ID 39 | - ``: Your Semaphore API token 40 | 41 | ## Accessing State Files 42 | 43 | You can access and manage your Terraform state files through the Semaphore UI: 44 | 45 | 1. Navigate to your project in Semaphore 46 | 2. Go to the "Terraform" section 47 | 3. Select "States" from the sidebar 48 | 4. View, download, or manage your state files 49 | 50 | ## Best Practices 51 | 52 | - Always use state locking when working in team environments 53 | - Regularly backup your state files 54 | - Use meaningful names for your state files 55 | - Keep your API tokens secure and rotate them periodically -------------------------------------------------------------------------------- /src/user-guide/task-templates/apps/terraform/workspaces.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Workspaces 8 | 9 | Semaphore provides built-in support for Terraform workspaces, allowing you to manage multiple environments and configurations within a single project. This feature helps you maintain separate state files for different environments like development, staging, and production. 10 | 11 | ## Features 12 | 13 | - **Workspace Management**: Create, switch, and delete workspaces directly from the Semaphore UI. 14 | - **State Isolation**: Each workspace maintains its own state file, preventing conflicts between environments. 15 | - **Environment Variables**: Configure workspace-specific environment variables. 16 | - **Workspace Selection**: Choose the target workspace when running Terraform commands. 17 | 18 | ## Using Workspaces in Semaphore 19 | 20 | ### Creating a Workspace 21 | 22 | In the **Workspaces** section of the Terraform/OpenTofu template where you want to add a workspace, follow these steps: 23 | 24 | 1. Click the ➕ button. 25 | 2. In the menu that appears, select **New Workspace**. 26 | 3. In the modal dialog, enter the workspace name and select the SSH key to be used for cloning modules. 27 | 4. Click the **Create** button to add the new workspace to the template. 28 | 5. You can now use this workspace to run tasks. 29 | 30 | ![](<../../../../.gitbook/assets/tf-workspace.webp>) 31 | 32 | ### Switching workspaces 33 | 34 | You can set the default workspace for a Terraform/OpenTofu template by clicking the **MAKE DEFAULT** button. 35 | 36 | ![](<../../../../.gitbook/assets/default-workspace.webp>) 37 | 38 | ### Workspace-specific variables 39 | 40 | Semaphore currently does not support workspace-specific variables. -------------------------------------------------------------------------------- /src/user-guide/task-templates/survey-vars.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | ![](https://www.semaphoreui.com/uploads/v2.14/survey.webp) -------------------------------------------------------------------------------- /src/user-guide/tasks.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | A task is an instance of launching an Ansible playbook. You can create the task from [Task Template](task-templates/) by clicking the button Run/Build/Deploy for the required template. 4 | 5 | ![](<../.gitbook/assets/image (6).png>) 6 | 7 | The **Deploy** task type allows you to specify a version of the build associated with the task. By default, it is the latest build version. 8 | 9 | ![](<../.gitbook/assets/task\_deploy (1).png>) 10 | 11 | When the task is running, or it has finished, you can see the task status and the running log. 12 | 13 | ![](<../.gitbook/assets/image (7).png>) 14 | 15 | ## Tasks log retention 16 | You'll notice that logs of previous runs of your tasks are available in the tasks template or in the dashboard. 17 | 18 | However, by default, log retention is infinite. 19 | 20 | You can configure this by using the `max_tasks_per_template` parameter in `config.json` or the `SEMAPHORE_MAX_TASKS_PER_TEMPLATE` environment variable. 21 | 22 | -------------------------------------------------------------------------------- /src/user-guide/team.md: -------------------------------------------------------------------------------- 1 | # Teams 2 | 3 | In Semaphore UI, every project is associated with a **Team**. Only team members and admins can access the project. Each member of the team is assigned one of four predefined roles, which govern their level of access and the actions they can perform. 4 | 5 |
6 | To avoid losing access to a project, it’s recommended to have at least two team members with the Owner role. 7 |
8 | 9 | --- 10 | 11 | ## Team roles 12 | 13 | Every team member has exactly one of these four roles: 14 | 15 | - **Owner** 16 | - **Manager** 17 | - **Task Runner** 18 | - **Guest** 19 | 20 | Below are detailed descriptions of each role and its permissions. 21 | 22 | ### Owner 23 | 24 | - **Full permissions**
25 | Owners can do anything within the project, including managing roles, adding/removing members, and configuring any project settings. 26 | 27 | - **Multiple owners**
28 | A project can have multiple Owners, ensuring there is more than one person with full privileges. 29 | 30 | - **Restrictions on self-removal**
31 | An Owner cannot remove themselves if they are the only Owner of the project. This prevents the project from being left without an Owner. 32 | 33 | - **Managing other wwners**
34 | Owners can manage (including remove or change roles of) all team members, including other Owners. 35 | 36 | ### Manager 37 | 38 | - **Broad project control:** Managers have almost the same permissions as Owners, allowing them to handle most day-to-day tasks and manage the project environment. 39 | 40 | - Managers **cannot**: 41 | - Remove the project. 42 | - Remove or change the roles of Owners. 43 | 44 | - **Typical use case:** Assign the Manager role to senior team members who need extensive access but don’t require the authority to delete the project or manage Owners. 45 | 46 | ### Task Runner 47 | 48 | - **Run tasks:** Task Runners can execute any task template that exists within the project. 49 | 50 | - **Read-only for other resources:** While they can run tasks, they only have read‐only access to other resources such as inventory, variables, repositories, etc. 51 | 52 | - **Typical use case:** Developers or QA engineers who need to trigger and monitor tasks but do not need the ability to modify project settings or manage team membership. 53 | 54 | ### Guest 55 | 56 | - **Read-only access:** Guests have read-only access to all project resources (e.g., viewing logs, inventories, dashboards). 57 | 58 | - **No write permissions:** They cannot modify settings, run tasks, or change roles. 59 | 60 | - **Typical use case:** Stakeholders or other collaborators who only need to view project status and details without making changes. 61 | 62 | --- 63 | 64 | ## Managing team members 65 | 66 | - **Inviting new members:** **Owners** and **Managers** can invite new users to join the team and assign them an initial role. 67 | 68 | - **Changing roles:** Owners can always change the roles of any team member. Managers can change the roles of **Task Runners** and **Guests**, but **not** other Managers or Owners. 69 | 70 | - **Removing members:** Owners and Managers can remove team members with lower roles. 71 | - An Owner can remove anyone (including other Owners), but cannot remove themselves if they are the sole Owner. 72 | - A Manager can remove **Task Runners** and **Guests**, but **not** other Managers or Owners. 73 | 74 | --- 75 | 76 | ## Best practices 77 | 78 | 1. **Maintain redundancy:** Assign the **Owner** role to at least two people to ensure continuous access and prevent a single point of failure. 79 | 2. **Follow principle of least privilege:** 80 | - Give team members the minimum role necessary for their tasks. 81 | - Use **Task Runner** or **Guest** roles for those who only need limited permissions. 82 | 3. **Review membership regularly:** 83 | - As team structures change, re‐evaluate roles. 84 | - Revoke access or downgrade roles for users who no longer need high‐level privileges. 85 | 4. **Use managers for day-to-day administration:** 86 | - Reserve the Owner role for a smaller group with ultimate authority. 87 | - Delegate routine project management tasks to Managers to reduce the risk of accidental major changes or project deletions. 88 | 89 | --- 90 | 91 | ## Frequently asked questions 92 | 93 | ### 1. Can an Owner remove another Owner? 94 | Yes, an Owner can remove or change the role of any other Owner, unless they are the only remaining Owner in the project. 95 | 96 | ### 2. Who can delete the project? 97 | Only **Owners** can delete a project. 98 | 99 | ### 3. Can Managers add or remove other Managers? 100 | No. Managers can only add or remove users with **Task Runner** or **Guest** roles. To manage Owners or other Managers, you must be an Owner. 101 | 102 | ### 4. What happens if I remove all Owners by accident? 103 | Semaphore UI prevents the removal of an Owner if it would leave the project with no Owners at all. There must be at least one Owner at all times. 104 | 105 | ### 5. Can Guests run tasks? 106 | No. Guests only have read‐only access and cannot trigger or manage tasks. 107 | -------------------------------------------------------------------------------- /theme/content.css: -------------------------------------------------------------------------------- 1 | .content img { 2 | border-radius: 6px; 3 | } 4 | 5 | .light .content img { 6 | box-shadow: 0 0 3px var(--searchbar-shadow-color); 7 | } 8 | 9 | hr { 10 | border: 0; 11 | border-bottom: 1px solid rgba(150,150,150,0.1); 12 | } 13 | 14 | .warning { 15 | color: var(--warning-border); 16 | } -------------------------------------------------------------------------------- /theme/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/theme/favicon.png -------------------------------------------------------------------------------- /theme/head.hbs: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | -------------------------------------------------------------------------------- /theme/header.css: -------------------------------------------------------------------------------- 1 | .menu-icon { 2 | width: 30px; 3 | transform: translateY(6px); 4 | margin-right: 5px; 5 | opacity: 0.7; 6 | } 7 | 8 | .light .menu-icon { 9 | opacity: 1; 10 | } 11 | 12 | 13 | .chapter > .chapter-item > .toggle { 14 | display: none; 15 | } 16 | 17 | svg { 18 | border-radius: 6px; 19 | } 20 | 21 | #disqus_thread { 22 | padding-top: 100px; 23 | } 24 | 25 | #disqus_thread > iframe { 26 | color-scheme: normal; 27 | } 28 | 29 | .menu-title { 30 | background-position: center; 31 | background-repeat: no-repeat; 32 | background-size: 150px; 33 | /* color: transparent; */ 34 | } 35 | 36 | .light .menu-title, 37 | .rust .menu-title { 38 | background-image: url(/.gitbook/assets/semaphore-docs-light.png); 39 | } 40 | 41 | .coal .menu-title, 42 | .navy .menu-title, 43 | .ayu .menu-title { 44 | background-image: url(/.gitbook/assets/semaphore-docs-dark.png); 45 | } -------------------------------------------------------------------------------- /theme/search.css: -------------------------------------------------------------------------------- 1 | ul#searchresults { 2 | padding-right: 10px; 3 | } 4 | 5 | ul#searchresults li { 6 | margin: 10px 0px; 7 | padding: 2px; 8 | border-radius: 2px; 9 | border-bottom: 1px dashed var(--searchresults-header-fg); 10 | padding-bottom: 10px; 11 | } 12 | 13 | ul#searchresults li:last-child { 14 | border-bottom: 0; 15 | padding-bottom: 0; 16 | } 17 | 18 | .searchresults-outer#searchresults-outer { 19 | border-radius: 6px; 20 | margin-top: 6px; 21 | border: 1px solid var(--searchbar-border-color); 22 | background-color: var(--searchbar-bg); 23 | /* box-shadow: 0 0 3px var(--searchbar-shadow-color); */ 24 | } 25 | 26 | ul#searchresults span.teaser { 27 | color: var(--searchbar-fg); 28 | margin-left: 0; 29 | } -------------------------------------------------------------------------------- /theme/tabs.css: -------------------------------------------------------------------------------- 1 | .mdbook-tabs { 2 | display: flex; 3 | } 4 | 5 | .mdbook-tab { 6 | background-color: var(--table-alternate-bg); 7 | padding: 0.5rem 1rem; 8 | cursor: pointer; 9 | border: none; 10 | font-size: 1.6rem; 11 | line-height: 1.45em; 12 | } 13 | 14 | .mdbook-tab.active { 15 | background-color: var(--table-header-bg); 16 | font-weight: bold; 17 | } 18 | 19 | .mdbook-tab-content { 20 | padding: 1rem 0rem; 21 | } 22 | 23 | .mdbook-tab-content table { 24 | margin: unset; 25 | } 26 | -------------------------------------------------------------------------------- /theme/tabs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Change active tab of tabs. 3 | * 4 | * @param {Element} container 5 | * @param {string} name 6 | */ 7 | const changeTab = (container, name) => { 8 | for (const child of container.children) { 9 | if (!(child instanceof HTMLElement)) { 10 | continue; 11 | } 12 | 13 | if (child.classList.contains('mdbook-tabs')) { 14 | for (const tab of child.children) { 15 | if (!(tab instanceof HTMLElement)) { 16 | continue; 17 | } 18 | 19 | if (tab.dataset.tabname === name) { 20 | tab.classList.add('active'); 21 | } else { 22 | tab.classList.remove('active'); 23 | } 24 | } 25 | } else if (child.classList.contains('mdbook-tab-content')) { 26 | if (child.dataset.tabname === name) { 27 | child.classList.remove('hidden'); 28 | } else { 29 | child.classList.add('hidden'); 30 | } 31 | } 32 | } 33 | }; 34 | 35 | document.addEventListener('DOMContentLoaded', () => { 36 | const tabs = document.querySelectorAll('.mdbook-tab'); 37 | for (const tab of tabs) { 38 | tab.addEventListener('click', () => { 39 | if (!(tab instanceof HTMLElement)) { 40 | return; 41 | } 42 | 43 | if (!tab.parentElement || !tab.parentElement.parentElement) { 44 | return; 45 | } 46 | 47 | const container = tab.parentElement.parentElement; 48 | const name = tab.dataset.tabname; 49 | const global = container.dataset.tabglobal; 50 | 51 | changeTab(container, name); 52 | 53 | if (global) { 54 | localStorage.setItem(`mdbook-tabs-${global}`, name); 55 | 56 | const globalContainers = document.querySelectorAll( 57 | `.mdbook-tabs-container[data-tabglobal="${global}"]` 58 | ); 59 | for (const globalContainer of globalContainers) { 60 | changeTab(globalContainer, name); 61 | } 62 | } 63 | }); 64 | } 65 | 66 | const containers = document.querySelectorAll('.mdbook-tabs-container[data-tabglobal]'); 67 | for (const container of containers) { 68 | const global = container.dataset.tabglobal; 69 | 70 | const name = localStorage.getItem(`mdbook-tabs-${global}`); 71 | if (name && document.querySelector(`.mdbook-tab[data-tabname=${name}]`)) { 72 | changeTab(container, name); 73 | } 74 | } 75 | }); 76 | --------------------------------------------------------------------------------