External Provisioners
By default, the Coder server runsbuilt-in provisioner daemons,which executeterraform
during workspace and template builds. However, thereare often benefits to running external provisioner daemons:
Secure build environments: Run build jobs in isolated containers,preventing malicious templates from gaining sh access to the Coder host.
Isolate APIs: Deploy provisioners in isolated environments (on-prem, AWS,Azure) instead of exposing APIs (Docker, Kubernetes, VMware) to the Coderserver. SeeProvider Authenticationfor more details.
Isolate secrets: Keep Coder unaware of cloud secrets, manage/rotatesecrets on provisioner servers.
Reduce server load: External provisioners reduce load and build queuetimes from the Coder server. SeeScaling Coder for moredetails.
Each provisioner runs a singleconcurrent workspace build.For example, running 30 provisioner containers will allow 30 users to startworkspaces at the same time.
Provisioners are started with thecoder provisioner start
command inthefull Coder binary. Keep readingto learn how to start provisioners via Docker, Kubernetes, Systemd, etc.
You can use the dashboard, CLI, or API tomanage provisioners.
Authentication
The provisioner daemon must authenticate with your Coder deployment.
We recommend creating finely-scoped keys for provisioners. Keys are scoped to anorganization, and optionally to a specific set of tags.
Use
coder provisioner
to create the key:To create a key for an organization that will match untagged jobs:
coder provisioner keys create my-key \ --org defaultSuccessfully created provisioner key my-key! Save this authentication token, it will not be shown again.<key omitted>
To restrict the provisioner to jobs with specific tags:
coder provisioner keys create kubernetes-key \ --org default \ --tag environment=kubernetesSuccessfully created provisioner key kubernetes-key! Save this authentication token, it will not be shown again.<key omitted>
Start the provisioner with the specified key:
export CODER_URL=https://<your-coder-url>export CODER_PROVISIONER_DAEMON_KEY=<key>coder provisioner start
Keep reading to see instructions for running provisioners onKubernetes/Docker/etc.
Provisioner Tags
You can useprovisioner tags to control which provisioners can pick up buildjobs from templates (and corresponding workspaces) with matching explicit tags.
Provisioners have two implicit tags:scope
andowner
. Coder sets these tagsautomatically.
- Organization-scoped provisioners always have the implicit tags
scope=organization owner=""
- User-scoped provisioners always have the implicit tags
scope=user owner=<uuid>
For example:
# Start a provisioner with the explicit tags# environment=on_prem and datacenter=chicagocoder provisioner start \ --tag environment=on_prem \ --tag datacenter=chicago# In another terminal, create/push# a template that requires the explicit# tag environment=on_premcoder templates push on-prem \ --provisioner-tag environment=on_prem# Or, match the provisioner's explicit tags exactlycoder templates push on-prem-chicago \ --provisioner-tag environment=on_prem \ --provisioner-tag datacenter=chicago
This can also be done in the UI when building a template:

Alternatively, a template can target a provisioner viaworkspace tagsinside the Terraform. See theworkspace tags documentationfor more information.
Note
Workspace tags defined with thecoder_workspace_tags
data sourcetemplatedo not automatically apply to the template import job! You mayneed to specify the desired tags when importing the template.
A provisioner can run a given build job if one of the below is true:
- A job with no explicit tags can only be run on a provisioner with no explicittags. This way you can introduce tagging into your deployment withoutdisrupting existing provisioners and jobs.
- If a job has any explicit tags, it can only run on a provisioner with thoseexplicit tags (the provisioner could have additional tags).
The external provisioner in the above example can run build jobs in the sameorganization with tags:
environment=on_prem
datacenter=chicago
environment=on_prem datacenter=chicago
However, it will not pick up any build jobs that do not have either of theenvironment
ordatacenter
tags set. It will also not pick up any build jobsfrom templates with the tagscope=user
set, or build jobs from templates indifferent organizations.
Note
If you only run tagged provisioners, you will need to specify a set oftags that matches at least one provisioner forall template import jobs andworkspace build jobs.
You may wish to run at least one additional provisioner with no additionaltags so that provisioner jobs with no additional tags defined will be pickedup instead of potentially remaining in the Pending state indefinitely.
This is illustrated in the below table:
Provisioner Tags | Job Tags | Same Org | Can Run Job? |
---|---|---|---|
scope=organization owner= | scope=organization owner= | ✅ | ✅ |
scope=organization owner= environment=on-prem | scope=organization owner= environment=on-prem | ✅ | ✅ |
scope=organization owner= environment=on-prem datacenter=chicago | scope=organization owner= environment=on-prem | ✅ | ✅ |
scope=organization owner= environment=on-prem datacenter=chicago | scope=organization owner= environment=on-prem datacenter=chicago | ✅ | ✅ |
scope=user owner=aaa | scope=user owner=aaa | ✅ | ✅ |
scope=user owner=aaa environment=on-prem | scope=user owner=aaa | ✅ | ✅ |
scope=user owner=aaa environment=on-prem | scope=user owner=aaa environment=on-prem | ✅ | ✅ |
scope=user owner=aaa environment=on-prem datacenter=chicago | scope=user owner=aaa environment=on-prem | ✅ | ✅ |
scope=user owner=aaa environment=on-prem datacenter=chicago | scope=user owner=aaa environment=on-prem datacenter=chicago | ✅ | ✅ |
scope=organization owner= | scope=organization owner= environment=on-prem | ✅ | ❌ |
scope=organization owner= environment=on-prem | scope=organization owner= | ✅ | ❌ |
scope=organization owner= environment=on-prem | scope=organization owner= environment=on-prem datacenter=chicago | ✅ | ❌ |
scope=organization owner= environment=on-prem datacenter=new_york | scope=organization owner= environment=on-prem datacenter=chicago | ✅ | ❌ |
scope=user owner=aaa | scope=organization owner= | ✅ | ❌ |
scope=user owner=aaa | scope=user owner=bbb | ✅ | ❌ |
scope=organization owner= | scope=user owner=aaa | ✅ | ❌ |
scope=organization owner= | scope=user owner=aaa environment=on-prem | ✅ | ❌ |
scope=user owner=aaa | scope=user owner=aaa environment=on-prem | ✅ | ❌ |
scope=user owner=aaa environment=on-prem | scope=user owner=aaa environment=on-prem datacenter=chicago | ✅ | ❌ |
scope=user owner=aaa environment=on-prem datacenter=chicago | scope=user owner=aaa environment=on-prem datacenter=new_york | ✅ | ❌ |
scope=organization owner= environment=on-prem | scope=organization owner= environment=on-prem | ❌ | ❌ |
Tip
To generate this table, run the following command andcopy the output:
go test -v -count=1 ./coderd/provisionerdserver/ -test.run='^TestAcquirer_MatchTags/GenTable$'
Types of provisioners
Provisioners can broadly be categorized by scope:organization
oruser
. Thescope of a provisioner can be specified with-tag=scope=<scope>
whenstarting the provisioner daemon. Only users with at least theTemplate Admin role or higher may createorganization-scoped provisioner daemons.
There are two exceptions:
- Built-in provisioners arealways organization-scoped.
- External provisioners started using apre-shared key (PSK) are alwaysorganization-scoped.
Organization-Scoped Provisioners
Organization-scoped Provisioners can pick up build jobs created by any user.These provisioners always have the implicit tagsscope=organization owner=""
.
coder provisioner start --org <organization_name>
If you omit the--org
argument, the provisioner will be assigned to thedefault organization.
coder provisioner start
User-scoped Provisioners
User-scoped Provisioners can only pick up build jobs created fromuser-tagged templates. Unlike the other provisioner types, any Coder user canrun user provisioners, but they have no impact unless there exists at least onetemplate with thescope=user
provisioner tag.
coder provisioner start \ --tag scope=user# In another terminal, create/push# a template that requires user provisionerscoder templates push on-prem \ --provisioner-tag scope=user
Example: Running an external provisioner with Helm
Coder provides a Helm chart for running external provisioner daemons, which youwill use in concert with the Helm chart for deploying the Coder server.
Create a provisioner key:
coder provisioner keys create my-cool-key --org default# Optionally, you can specify tags for the provisioner key:# coder provisioner keys create my-cool-key --org default --tag location=auh --tag kind=k8sSuccessfully created provisioner key kubernetes-key! Save this authenticationtoken, it will not be shown again.<key omitted>
Store the key in a kubernetes secret:
kubectl create secret generic coder-provisioner-keys --from-literal=my-cool-key=`<key omitted>`
Create a
provisioner-values.yaml
file for the provisioner daemons Helmchart. For example:coder: env: - name: CODER_URL value: "https://coder.example.com" replicaCount: 10provisionerDaemon: # NOTE: in older versions of the Helm chart (2.17.0 and below), it is required to set this to an empty string. pskSecretName: "" keySecretName: "coder-provisioner-keys" keySecretKey: "my-cool-key"
This example creates a deployment of 10 provisioner daemons (for 10concurrent builds) authenticating using the above key. The daemons willauthenticate using the provisioner key created in the previous step andacquire jobs matching the tags specified when the provisioner key wascreated. The set of tags is inferred automatically from the provisioner key.
Refer to thevalues.yamlfile for the coder-provisioner chart for information on what values can bespecified.
Install the provisioner daemon chart
helm install coder-provisioner coder-v2/coder-provisioner \ --namespace coder \ --version <your version> \ --values provisioner-values.yaml
You can verify that your provisioner daemons have successfully connected toCoderd by looking for a debug log message that says
provisioner: successfully connected to coderd
from each Pod.
Example: Running an external provisioner on a VM
curl -L https://coder.com/install.sh | shexport CODER_URL=https://coder.example.comexport CODER_SESSION_TOKEN=your_tokencoder provisioner start
Example: Running an external provisioner via Docker
docker run --rm -it \ -e CODER_URL=https://coder.example.com/ \ -e CODER_SESSION_TOKEN=your_token \ --entrypoint /opt/coder \ ghcr.io/coder/coder:latest \ provisioner start
Disable built-in provisioners
As mentioned above, the Coder server will run built-in provisioners by default.This can be disabled with a server-wideflag or environment variable.
coder server --provisioner-daemons=0
Prometheus metrics
Coder provisioner daemon exports metrics via the HTTP endpoint, which can beenabled using either the environment variableCODER_PROMETHEUS_ENABLE
or theflag--prometheus-enable
.
The Prometheus endpoint address ishttp://localhost:2112/
by default. You canuse either the environment variableCODER_PROMETHEUS_ADDRESS
or the flag--prometheus-address <network-interface>:<port>
to select a different listenaddress.
If you have provisioners daemons deployed as pods, it is advised to monitor themseparately.