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

Semaphore vs AWX vs AAP

FeatureSemaphoreAWXAAP
LicenseMITApache 2.0Subscription
Install complexityLow (single binary)High (Kubernetes)Medium (installer)
Resource usage~100 MB RAM4+ GB RAM16+ GB RAM
Web UIModern, simpleFull-featuredEnterprise
RBACBasic (teams)FullFull
Workflows
Dynamic inventory❌ (static/file only)
Execution Environments
Automation Mesh
Event-Driven
APIRESTRESTREST
NotificationsSlack, Telegram, emailMultipleMultiple
SchedulingCronCron + RRULECron + RRULE
Best forSmall teams, homelabsMedium teams, dev/testEnterprise 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.

See also: Automating Jenkins Installation with Ansible

Install Semaphore

# 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

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"

See also: A Preview of Ansible Journey in 2024

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"

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

See also: Manage Ansible Collection Changelogs with Antsibull-Changelog

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.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home