Semaphore UI
Semaphore UI is a web-based interface for running and scheduling Ansible playbooks, Bash and Python scripts.
This compose file deploys Semaphore UI with a PostgreSQL database. All data is stored on NFS volumes, and the web interface is proxied through a Traefik reverse proxy with Cloudflare TLS certificates.
Docker Compose
# compose.yaml
services:
semaphore:
image: semaphoreui/semaphore:latest
container_name: semaphore
restart: unless-stopped
depends_on:
semaphore-db:
condition: service_healthy
# ports:
# - 3000:3000
networks:
- semaphore
- semaphore_proxy
volumes:
- type: volume
source: docker-nfs
target: /var/lib/semaphore
volume:
subpath: semaphore/data
- type: volume
source: docker-nfs
target: /etc/semaphore
volume:
subpath: semaphore/config
environment:
SEMAPHORE_DB_USER: ${POSTGRES_USER}
SEMAPHORE_DB_PASS: ${POSTGRES_PASSWORD}
SEMAPHORE_DB_HOST: semaphore-db
SEMAPHORE_DB_PORT: 5432
SEMAPHORE_DB_DIALECT: postgres
SEMAPHORE_DB: ${POSTGRES_DB}
SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/
SEMAPHORE_ADMIN_PASSWORD: ${SEMAPHORE_ADMIN_PASSWORD}
SEMAPHORE_ADMIN_NAME: ${SEMAPHORE_ADMIN_NAME}
SEMAPHORE_ADMIN_EMAIL: ${SEMAPHORE_ADMIN_EMAIL}
SEMAPHORE_ADMIN: ${SEMAPHORE_ADMIN}
SEMAPHORE_ACCESS_KEY_ENCRYPTION: ${SEMAPHORE_ACCESS_KEY_ENCRYPTION}
SEMAPHORE_LDAP_ACTIVATED: "no"
TZ: Europe/London
labels:
- "traefik.enable=true"
- "traefik.docker.network=semaphore_proxy"
- "traefik.http.services.semaphore.loadbalancer.server.port=3000"
- "traefik.http.routers.semaphore.rule=Host(`semaphore.${TRAEFIK_BASE_URL}`)"
- "traefik.http.routers.semaphore.entrypoints=websecure"
- "traefik.http.routers.semaphore.tls=true"
- "traefik.http.routers.semaphore.tls.certresolver=cloudflare"
semaphore-db:
image: postgres:18
container_name: semaphore-db
restart: unless-stopped
networks:
- semaphore
volumes:
- type: volume
source: docker-nfs
target: /var/lib/postgresql
volume:
subpath: semaphore/postgres
healthcheck:
test:
["CMD", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
TZ: Europe/London
volumes:
docker-nfs:
driver: local
driver_opts:
type: nfs
o: addr=xxx.xxx.xxx.xxx,nolock,soft,rw,nfsvers=4.2
device: :/mnt/nfs-volume
networks:
semaphore:
name: semaphore
semaphore_proxy:
name: semaphore_proxy
Environment Variables
# .env
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_DB=
SEMAPHORE_ADMIN_PASSWORD=
SEMAPHORE_ADMIN_NAME=
SEMAPHORE_ADMIN_EMAIL=
SEMAPHORE_ADMIN=
SEMAPHORE_ACCESS_KEY_ENCRYPTION= # generated with `openssl rand -base64 32`
TRAEFIK_BASE_URL=example.com
Traefik Configuration
# compose.yaml (excerpt)
services:
traefik:
image: traefik:latest
container_name: traefik
...
networks:
- traefik
# here
- semaphore_proxy
...
networks:
# here
semaphore_proxy:
name: semaphore_proxy