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 Use • AAP 2.6 Architecture and Components • Ansible vs Terraform Complete Comparison • Install Ansible Complete Guide • Ansible Documentation Complete GuideCategory: installation