# semaphore Deploys [SemaphoreUI](https://semaphoreui.com) with a PostgreSQL backend on the mk-labs `imagineering` VM (`figment`) via rootful Podman Quadlets. ## Architecture ``` ┌─────────────────────┐ user ─── HTTPS ─── Traefik ──▶│ figment (10.1.71.37) │ │ │ │ ┌─────────────────┐ │ │ │ semaphore │ │ │ │ :3000 │──┼──┐ │ └─────────────────┘ │ │ semaphore-net │ │ │ (podman) │ ┌─────────────────┐ │ │ │ │ semaphore- │◀─┼──┘ │ │ postgres :5432 │ │ │ └─────────────────┘ │ │ │ │ /etc/containers/ │ │ systemd/*.container│ └───────────────────────┘ ``` - Both containers run as **rootful** Podman services, managed by systemd-generated units from Quadlet files in `/etc/containers/systemd/`. - The two containers share a user-defined Podman network (`semaphore-net`) so Semaphore can address PostgreSQL by container name. - Data persists on two named Podman volumes (`semaphore_data`, `semaphore_postgres_data`) so container recreation is non-destructive. - Image tags are **pinned** — no floating `:latest`. ## Required vault variables | Variable | Purpose | |-----------------------------------------|---------------------------------------------------------| | `vault_semaphore_database_password` | PostgreSQL role password for the `semaphore` DB user. | | `vault_semaphore_admin_password` | Initial admin password (used during manual user-add). | | `vault_semaphore_access_key_encryption` | 32-byte key for Semaphore-stored access keys. | Generate the access-key encryption value with: ```bash head -c 32 /dev/urandom | base64 ``` ## Usage ```yaml - name: Deploy SemaphoreUI hosts: semaphore_server become: true roles: - semaphore ``` Or via the dedicated playbook: ```bash ansible-playbook -i inventory.yml playbooks/day1_deploy_semaphore.yml ``` ## First-run admin user The container provisions the initial admin user from the `SEMAPHORE_ADMIN_*` environment variables on first boot. Credentials: | Field | Value | |----------|------------------------------------------------| | Login | `admin` | | Name | `Administrator` | | Email | `admin@local.mk-labs.cloud` | | Password | `vault_semaphore_admin_password` (in vault) | The admin env vars are only consulted on first boot when no admin exists in the database. Subsequent password rotations should be done through the web UI, not by re-running this role. ## Pinned versions - **Semaphore**: `docker.io/semaphoreui/semaphore:v2.18.5-ansible2.16.5` (bundles Ansible 2.16.5 inside the container, matching our controller) - **PostgreSQL**: `docker.io/library/postgres:16-alpine` To bump versions, update `semaphore_image` / `semaphore_postgres_image` in `defaults/main.yml`. Test against figment, snapshot first. ## Verification The role completes with an HTTP health check against `http://127.0.0.1:3000/api/ping`. If Semaphore doesn't respond within ~60 seconds, the role fails loudly rather than reporting green-but-broken. ## Troubleshooting ```bash # Container status sudo systemctl status semaphore semaphore-postgres sudo podman ps -a # Logs sudo journalctl -u semaphore -f sudo journalctl -u semaphore-postgres -f sudo podman logs semaphore --tail 100 # Network sudo podman network inspect semaphore-net # Wipe and redeploy (destructive — destroys all Semaphore data) sudo systemctl stop semaphore semaphore-postgres sudo podman volume rm semaphore_data semaphore_postgres_data sudo rm /etc/containers/systemd/semaphore.container /etc/containers/systemd/semaphore-postgres.container sudo systemctl daemon-reload # then re-run the playbook ```