Skip to main content

Executors

An executor decides where a runner starts the task process. The runner still polls the server, receives the task and sends the log back; only the place the Ansible, Terraform or shell process runs changes.

Executorrunner.executor.typeWhere a task runsEdition
Locallocal (default)A child process on the runner host, with the runner's files, tools and PATH.Community
DockerdockerA new container on a Docker daemon, removed after the task.Pro
Kubernetesk8sA new Pod in a Kubernetes namespace, deleted after the task.Enterprise

The executor is a setting of the runner, not of the server or the template. The Semaphore server itself always runs tasks locally; to run tasks in containers, start a runner and route the templates to it with a runner tag.

Choose an executor​

You needUse
Tools installed on one machine, access to its files, workflow outputsLocal
A clean environment for every task, different toolchains per template, on one or a few hostsDocker
Tasks scheduled across a cluster, Kubernetes RBAC and Secrets, no Docker socketKubernetes

Set the executor​

Add the executor block to the runner section of the runner configuration file:

{
"web_host": "https://semaphore.example.com",
"runner": {
"token_file": "/var/lib/semaphore/runner_token.txt",
"executor": {
"type": "docker"
}
}
}

Or set the environment variable:

SEMAPHORE_RUNNER_EXECUTOR_TYPE=docker   # or k8s

Every option has an environment variable; the full lists are on the Docker and Kubernetes pages. Restart the runner after changing the executor.

info

The container executors are part of the Pro build of the runner. Use the semaphoreui/runner image or the official semaphore binary. The semaphoreui/runner-community image is built without them and logs docker executor is only available in the proprietary build at startup.

How a container task runs​

Both container executors run a task the same way:

  1. Prepare. The runner creates a shared workspace and puts the task's secrets (SSH keys, vault passwords, extra variables, a static inventory) where the containers can read them.
  2. Clone. A short-lived helper container clones the repository into /workspace/repo (git clone --depth 1 of the task's branch).
  3. Build. The build container starts in /workspace/repo and runs the app: ansible-playbook, terraform/tofu/terragrunt, bash, python3, powershell, or the binary of a custom app. Its exit code is the task result.
  4. Log. The output of both containers is streamed into the task log while they run.
  5. Clean up. Containers, volumes, Secrets and ConfigMaps of the task are removed, whether the task succeeded, failed or was stopped.
StepDockerKubernetes
WorkspaceNamed volume semaphore-workspace-<task>-<random>emptyDir volume of the Pod
SecretsNamed volume semaphore-secrets-<task>-<random>, mounted read-onlyOne Secret or ConfigMap per kind, mounted read-only
CloneContainer semaphore-job-<task>-<random>-cloneInit container git-clone
BuildContainer semaphore-job-<task>-<random>-buildContainer build of Pod semaphore-job-<task>-<random>
Stop buttondocker stop with cleanup_grace_seconds, then removePod deleted with cleanup_grace_seconds

A task stopped from the UI ends in the Stopped status, as with the local executor.

Inside the build container​

PathContent
/workspaceWritable workspace; also HOME of the task.
/workspace/repoThe cloned repository; the working directory.
/secrets/ssh/<key id>/id_rsaSSH private key of the repository or the inventory. Loaded into ssh-agent before the app starts.
/secrets/vault/<name>Ansible vault password, passed as --vault-id=<name>@<path>.
/secrets/extra-vars/values.jsonVariable Group extra variables plus semaphore_vars, passed as --extra-vars @<path>.
/secrets/inventory/inventory.cfg or inventory.ymlA static inventory stored in Semaphore. File inventories are read from the repository.

The container receives a fixed set of environment variables, listed in Task environment variables. The runner's own env_vars, forwarded_env_vars, PATH and apps.* paths are not passed into the container.

Images​

Two images take part in every task:

ImageDefaultUsed forNeeds
Build imagesemaphoreui/job:latestRunning the app.sh, the app binary on PATH; for SSH keys ssh-agent, ssh-add, setsid.
Helper imagesemaphoreui/helper:latestCloning the repository (Docker also uses it to fill the secrets volume).sh, git, ssh-agent, ssh-add.

semaphoreui/job is Debian with Ansible 9 (ansible-core 2.16–2.17), Python 3, OpenSSH, Git, Terraform, OpenTofu and Terragrunt. It runs as user nobody, group 0.

The build image is set per runner (image) and can be overridden per template with the Executor image field, see Executor image. The helper image is set per runner only.

Build a custom image​

Extend semaphoreui/job when your tasks need extra Ansible collections, Python packages or tools. Container executors do not install requirements.yml from the repository, so bake collections and roles into the image:

FROM semaphoreui/job:latest

USER root
RUN pip3 install --no-cache-dir --break-system-packages hvac boto3 \
&& ansible-galaxy collection install -p /usr/share/ansible/collections \
community.general amazon.aws
USER nobody:0
docker build -t registry.example.com/semaphore/job-aws:1.0 .
docker push registry.example.com/semaphore/job-aws:1.0

If you build an image from scratch, keep these properties of semaphoreui/job:

  • /workspace exists and is writable by the user the container runs as.
  • /etc/passwd is group-writable (chmod g=u /etc/passwd). A container started with a random UID (OpenShift) then gets a passwd entry, which OpenSSH and Git require.
  • The image does not need a special entrypoint: the executor runs its script with sh -c.

Limitations​

Compared with the local executor, the container executors currently:

Not supportedWhat to do instead
Inventory credentials of type Login with password (SSH password or become password)Use an SSH key. A task with such a key fails with inventory login_password authentication is not supported.
Terraform tasks that wait for manual confirmationEnable Auto approve in the template, or run plan-only tasks. Otherwise the task fails with terraform manual approval is not supported.
Repositories of type LocalThe workspace stays empty; use a Git repository.
Installing requirements.yml (Galaxy roles and collections)Bake them into the image.
Workflow outputs (SEMAPHORE_OUTPUTS_FILE)Use the local executor for tasks whose outputs a workflow reads.
Per-host credentials from host configUse the local executor.
Git history beyond the last commitThe clone is shallow (--depth 1).

SSH host keys are not checked: the clone accepts a new host key, and Ansible runs with ANSIBLE_HOST_KEY_CHECKING=False.