AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Semaphore: Open-Source UI for Ansible — Install, Configure, and Use

By Luca Berton · Published 2024-01-01 · Category: installation

Complete guide to Ansible Semaphore, the lightweight open-source web UI for Ansible. Install Semaphore, configure projects, inventories, and task templates.

What Is Ansible Semaphore?

Semaphore is a lightweight, open-source web UI for running Ansible playbooks. It provides a modern dashboard for managing Ansible automation without the complexity of AWX or the cost of AAP.

Key features: • Clean, modern web interface • Task scheduling (cron-like) • Team access with LDAP/OIDC support • Telegram, Slack, email notifications • REST API for integration • Written in Go — single binary, low resource usage

See also: Automating Jenkins Installation with Ansible

Semaphore vs AWX vs AAP

| Feature | Semaphore | AWX | AAP | |---------|-----------|-----|-----| | License | MIT | Apache 2.0 | Subscription | | Install complexity | Low (single binary) | High (Kubernetes) | Medium (installer) | | Resource usage | ~100 MB RAM | 4+ GB RAM | 16+ GB RAM | | Web UI | Modern, simple | Full-featured | Enterprise | | RBAC | Basic (teams) | Full | Full | | Workflows | ❌ | ✅ | ✅ | | Dynamic inventory | ❌ (static/file only) | ✅ | ✅ | | Execution Environments | ❌ | ❌ | ✅ | | Automation Mesh | ❌ | ❌ | ✅ | | Event-Driven | ❌ | ❌ | ✅ | | API | REST | REST | REST | | Notifications | Slack, Telegram, email | Multiple | Multiple | | Scheduling | Cron | Cron + RRULE | Cron + RRULE | | Best for | Small teams, homelabs | Medium teams, dev/test | Enterprise production |

Choose Semaphore when: You want a simple Ansible UI with minimal infrastructure, don't need workflows or dynamic inventory, and value low resource usage.

Choose AWX when: You need more features (workflows, RBAC, dynamic inventory) and have Kubernetes available.

Choose AAP when: Enterprise requirements — support, compliance, Automation Mesh, EDA.

Install Semaphore

Docker / Podman (Recommended)

# docker-compose.yml
version: "3"
services:
  semaphore:
    image: semaphoreui/semaphore:latest
    container_name: semaphore
    ports:
      - "3000:3000"
    environment:
      SEMAPHORE_DB_DIALECT: bolt
      SEMAPHORE_ADMIN_PASSWORD: "${SEMAPHORE_ADMIN_PASSWORD}"
      SEMAPHORE_ADMIN_NAME: admin
      SEMAPHORE_ADMIN_EMAIL: admin@example.com
      SEMAPHORE_ADMIN: admin
      SEMAPHORE_ACCESS_KEY_ENCRYPTION: "${SEMAPHORE_ACCESS_KEY_ENCRYPTION}"
    volumes:
      - semaphore-data:/var/lib/semaphore
      - semaphore-config:/etc/semaphore
      - semaphore-tmp:/tmp/semaphore

volumes: semaphore-data: semaphore-config: semaphore-tmp:

# Generate encryption key
export SEMAPHORE_ACCESS_KEY_ENCRYPTION=$(head -c32 /dev/urandom | base64)
export SEMAPHORE_ADMIN_PASSWORD="your-secure-password"

docker compose up -d

# Access at http://localhost:3000

Docker with PostgreSQL (Production)

# docker-compose.yml
version: "3"
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: semaphore
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
      POSTGRES_DB: semaphore
    volumes:
      - postgres-data:/var/lib/postgresql/data

semaphore: image: semaphoreui/semaphore:latest ports: - "3000:3000" environment: SEMAPHORE_DB_DIALECT: postgres SEMAPHORE_DB_HOST: postgres SEMAPHORE_DB_PORT: 5432 SEMAPHORE_DB_USER: semaphore SEMAPHORE_DB_PASS: "${POSTGRES_PASSWORD}" SEMAPHORE_DB: semaphore SEMAPHORE_ADMIN_PASSWORD: "${SEMAPHORE_ADMIN_PASSWORD}" SEMAPHORE_ADMIN_NAME: admin SEMAPHORE_ADMIN_EMAIL: admin@example.com SEMAPHORE_ADMIN: admin SEMAPHORE_ACCESS_KEY_ENCRYPTION: "${SEMAPHORE_ACCESS_KEY_ENCRYPTION}" volumes: - semaphore-config:/etc/semaphore - semaphore-tmp:/tmp/semaphore depends_on: - postgres

volumes: postgres-data: semaphore-config: semaphore-tmp:

Binary Install (No Docker)

# Download latest release
SEMAPHORE_VERSION=$(curl -s https://api.github.com/repos/semaphoreui/semaphore/releases/latest | grep tag_name | cut -d'"' -f4 | sed 's/v//')
wget "https://github.com/semaphoreui/semaphore/releases/download/v${SEMAPHORE_VERSION}/semaphore_${SEMAPHORE_VERSION}_linux_amd64.deb"

# Install sudo dpkg -i semaphore_${SEMAPHORE_VERSION}_linux_amd64.deb

# Initial setup semaphore setup

# Run semaphore server --config /etc/semaphore/config.json

Install with Ansible (Meta!)

---
- name: Install Semaphore with Ansible
  hosts: semaphore_server
  become: true

vars: semaphore_version: "2.10.22" semaphore_admin_user: admin semaphore_admin_password: "{{ vault_semaphore_password }}" semaphore_db_dialect: bolt semaphore_port: 3000

tasks: - name: Install dependencies ansible.builtin.apt: name: - ansible - git state: present update_cache: true

- name: Download Semaphore ansible.builtin.get_url: url: "https://github.com/semaphoreui/semaphore/releases/download/v{{ semaphore_version }}/semaphore_{{ semaphore_version }}_linux_amd64.deb" dest: "/tmp/semaphore.deb"

- name: Install Semaphore ansible.builtin.apt: deb: /tmp/semaphore.deb

- name: Generate config ansible.builtin.template: src: semaphore-config.json.j2 dest: /etc/semaphore/config.json mode: '0600'

- name: Create systemd service ansible.builtin.copy: content: | [Unit] Description=Semaphore Ansible UI After=network.target [Service] Type=simple ExecStart=/usr/bin/semaphore server --config /etc/semaphore/config.json Restart=always [Install] WantedBy=multi-user.target dest: /etc/systemd/system/semaphore.service notify: restart semaphore

- name: Start Semaphore ansible.builtin.systemd: name: semaphore state: started enabled: true daemon_reload: true

handlers: - name: restart semaphore ansible.builtin.systemd: name: semaphore state: restarted

See also: A Preview of Ansible Journey in 2024

Configure Semaphore

1. Create a Project

Projects group related automation. Each project has its own: • Key Store (SSH keys, passwords) • Repositories (Git repos with playbooks) • Inventory (host definitions) • Environment (variables) • Task Templates (playbook + inventory + environment)

2. Add Keys

UI: Project → Key Store → New Key

Type: SSH Key Name: "Server SSH Key" Private Key: (paste your private key)

Or via API:

curl -X POST "http://localhost:3000/api/project/1/keys" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Server SSH Key",
    "type": "ssh",
    "ssh": {
      "private_key": "'"$(cat ~/.ssh/id_ed25519)"'"
    }
  }'

3. Add Repository

UI: Project → Repositories → New Repository

Name: "Infrastructure Playbooks" URL: https://github.com/company/ansible-infra.git Branch: main Access Key: (select SCM key)

4. Add Inventory

UI: Project → Inventory → New Inventory

Name: "Production" Type: Static YAML Content: all: children: webservers: hosts: web-01: ansible_host: 10.0.1.10 web-02: ansible_host: 10.0.1.11

5. Add Environment

UI: Project → Environment → New Environment

Name: "Production Variables" Extra Variables: { "env": "production", "app_version": "2.1.0", "deploy_user": "deploy" }

6. Create Task Template

UI: Project → Task Templates → New Template

Name: "Deploy Web Application" Playbook: deploy.yml Repository: Infrastructure Playbooks Inventory: Production Environment: Production Variables SSH Key: Server SSH Key

7. Schedule Tasks

UI: Task Template → Edit → Schedules

Cron: 0 2 * * * (daily at 2 AM) Name: "Nightly Security Patching"

Semaphore API

# Authenticate
TOKEN=$(curl -s -X POST "http://localhost:3000/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"auth": "admin", "password": "your-password"}' | jq -r '.token')

# List projects curl -s "http://localhost:3000/api/projects" \ -H "Authorization: Bearer $TOKEN" | jq

# Run a task curl -X POST "http://localhost:3000/api/project/1/tasks" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"template_id": 1}'

# Get task status curl -s "http://localhost:3000/api/project/1/tasks/42" \ -H "Authorization: Bearer $TOKEN" | jq '{status, start, end}'

# Get task output curl -s "http://localhost:3000/api/project/1/tasks/42/output" \ -H "Authorization: Bearer $TOKEN"

See also: Manage Ansible Collection Changelogs with Antsibull-Changelog

Production Setup

Reverse Proxy with Nginx

server {
    listen 443 ssl http2;
    server_name semaphore.company.com;

ssl_certificate /etc/letsencrypt/live/semaphore.company.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/semaphore.company.com/privkey.pem;

location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }

location /api/ws { proxy_pass http://127.0.0.1:3000/api/ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }

LDAP Authentication

{
  "ldap_enable": true,
  "ldap_binddn": "cn=semaphore,ou=service,dc=company,dc=com",
  "ldap_bindpassword": "password",
  "ldap_server": "ldap://ldap.company.com:389",
  "ldap_searchdn": "ou=users,dc=company,dc=com",
  "ldap_searchfilter": "(&(objectClass=person)(uid=%s))",
  "ldap_mappings": {
    "dn": "dn",
    "mail": "mail",
    "uid": "uid",
    "cn": "cn"
  }
}

Backup

# BoltDB backup (default)
cp /var/lib/semaphore/database.boltdb /backup/semaphore-$(date +%Y%m%d).boltdb

# PostgreSQL backup pg_dump -U semaphore semaphore > /backup/semaphore-$(date +%Y%m%d).sql

FAQ

Is Ansible Semaphore free?

Yes. Semaphore is MIT licensed — completely free for any use, including commercial. There's no paid tier or enterprise edition.

Can Semaphore replace AWX?

For simple use cases (running playbooks with a web UI, basic scheduling, team access), yes. For advanced needs (workflow chains, dynamic inventory, RBAC granularity, API-driven automation at scale), AWX or AAP is better suited.

Does Semaphore support Ansible collections?

Yes. Semaphore runs standard Ansible, so any collections installed on the server (or defined in requirements.yml in your repository) work. Add a collections/requirements.yml to your playbook repo and Semaphore installs them automatically.

How many concurrent tasks can Semaphore handle?

Semaphore runs tasks sequentially per project by default. You can configure max_parallel_tasks in the config to allow concurrent execution. Resource usage scales linearly — each task spawns an ansible-playbook process.

Conclusion

Ansible Semaphore gives you a clean, lightweight web UI for Ansible automation without the infrastructure overhead of AWX or the cost of AAP. Install it as a single binary or Docker container, point it at your playbook Git repository, and you have a scheduling, notification, and team collaboration layer for your Ansible automation in minutes.

Related Articles

AWX Complete Guide: Install, Configure, and UseAAP 2.6 Architecture and ComponentsAnsible vs Terraform Complete ComparisonInstall Ansible Complete GuideAnsible Documentation Complete Guide

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home