From 6b75c2e461419591c38e903eab6a0a844ce22014 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Mon, 28 Apr 2025 14:42:36 +0200 Subject: [PATCH 01/17] chore: prebuild docs Signed-off-by: Danny Kopping --- .../prebuilt-workspaces.md | 199 ++++++++++++++++++ .../prebuilt-workspaces.png | Bin 0 -> 41287 bytes .../replacement-notification.png | Bin 0 -> 73977 bytes 3 files changed, 199 insertions(+) create mode 100644 docs/admin/templates/extending-templates/prebuilt-workspaces.md create mode 100644 docs/admin/templates/extending-templates/prebuilt-workspaces.png create mode 100644 docs/admin/templates/extending-templates/replacement-notification.png diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md new file mode 100644 index 0000000000000..3f293aa369dd6 --- /dev/null +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -0,0 +1,199 @@ +# Prebuilt workspaces (beta) + +## Overview + +Prebuilt workspaces let you pre-provision and maintain a pool of ready-to-claim workspaces. +When a developer requests a workspace matching a preset, Coder assigns an existing instance instead of creating a new +one, reducing setup time significantly. + +## Prerequisites + +- Premium license +- Use `coder/coder` Terraform provider `>= 2.3.0-pre2` in your template (**TODO: update with latest version**) +- Enable the `workspace-prebuilds` [experiment](https://coder.com/docs/reference/cli/server#--experiments) + +## Configuration + +1. In your Terraform template, add a `prebuilds` block within a `coder_workspace_preset` block: + + ```hcl + data "coder_workspace_preset" "goland" { + name = "GoLand: Large" + parameters = { + jetbrains_ide = "GO" + cpus = 8 + memory = 16 + } + prebuilds { + instances = 3 + } + } + ``` + +2. Publish and import the template +3. An internal reconciliation loop maintains exactly the specified `instances` of prebuilt workspaces. + +_This model of declarative configuration plus a reconciliation loop is similar to Kubernetes._ + +## Ownership + +When prebuilt workspaces are created, they are owned by the pseudo-user `prebuilds`. This user has no permissions, and +is simply a mechanism to identify unclaimed prebuilt workspaces. + +The `prebuilds` user is as a member of the `Everyone` group, and can be added to other groups. + +## Viewing prebuilt workspaces + +Given that prebuilt workspaces are just regular workspaces, you can view them in the **Workspaces** view in the +frontend: + +![prebuilt-workspaces.png](prebuilt-workspaces.png) + +## Claiming + +A prebuilt workspace is automatically and transparently assigned to a user when the following occurs: + +1. The user creates a new workspace via the API or the Coder web UI +2. The user selected a preset in #1 which has been configured for prebuilds +3. A prebuilt workspace is in eligible state + +The ownership of the prebuilt workspace will change to the requesting user, and this is referred to as a "claim". + +## Eligibility + +When a prebuilt workspace is running, and its agent has completed all of its bootstrap procedures and executed its +startup scripts, the workspace will be marked eligible to be claimed. + +## Relationship to workspace presets + +[Workspace presets](https://coder.com/docs/admin/templates/extending-templates/parameters#workspace-presets-beta) allow +you to configure commonly used combinations of parameters into a single option, which makes it easier for developers to +pick one that fits +their needs. + +Prebuilt workspaces need to have a preset defined to match the _base configuration_ of a workspace, i.e. the preset +needs to define all the required parameters needed to build a workspace. These parameters are necessary in order to +build workspaces in the background. + +Parameters which are not required or not part of a preset can still be used with prebuilt workspaces. The preset defines +the minimum required set of parameters, and these are immutable. + +## Invalidation + +Prebuilt workspaces are _never_ updated after they are created. + +Whenever a template version changes, all prebuilt workspaces relating to an inactive template version will be destroyed. +New prebuilt workspaces will be provisioned for the active template version. + +Invalidating prebuilt workspaces is useful when your template version does not change but a referenced dependency does, +which is necessary for running an up-to-date workspace. For example, if +an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) which is referenced by your template is updated, +you can simply delete the prebuilt workspaces, and they will be recreated with the latest AMI. + +_In future releases, we will allow operators to invalidate their prebuilt workspaces programmatically._ + +## Quotas + +Prebuilt workspaces can be used in conjunction with [Resource Quotas](https://coder.com/docs/admin/users/quotas). Given +that all unclaimed prebuilt workspaces are [owned](#ownership) by the `prebuilds` user, you may configure a quota for +any group which this user appears in. + +Once the quota is exceeded, prebuilt workspaces will fail provisioning like regular workspaces would. + +## Current Limitations + +### Organizations + +Prebuilt workspaces can only be utilized by the default organization. + +https://github.com/coder/internal/issues/364 is open to track this feature, and will be implemented in a future release. + +### Autoscaling + +Prebuilt workspaces will remain running indefinitely until they are claimed. We do not at present have an autoscaling +mechanism to reduce the number of instances after working hours. + +https://github.com/coder/internal/issues/312 is open to track this feature, and will be implemented in a future release. + +## Gotchas + +### Resource Replacement + +When a prebuilt workspace is created, it is initially [owned](#ownership) by the `prebuilds` user and a random name +is generated for it. When `terraform apply` runs, it will provide these values during provisioning in the +[`coder_workspace`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace) and +[`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner) +datasources. + +Once a prebuilt workspace is claimed, the ownership of that workspace changes to the requesting user and +`terraform apply` is run again, now with updated values for the aforementioned datasources. + +If your template has used these datasources in immutable fields (i.e. the +[`user_data`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#user_data-1) field in +an `aws_instance` resource), Terraform will interpret these changes as _drift_ and will therefore destroy and recreate +the resource. + +This is obviously undesirable because the prebuilt workspace will now have to provision _again_, while the user waits, +eliminating the value of the prior pre-provisioning. + +Should this occur when a prebuilt workspace is claimed, all Template Admins will receive a notification which will +link them to the build logs to investigate which resource was being replaced. + +![replacement-notification.png](replacement-notification.png) + +To avoid this problem, you will need to add a `lifecycle` block to your resource: + +```hcl +resource "docker_container" "workspace" { + lifecycle { + ignore_changes = all + } + + count = data.coder_workspace.me.start_count + name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" + ... +} +``` + +In the above example, the `docker_container` would be created with a `name` attribute which includes references to the +initial owner (i.e. `prebuilds`), and will never change - even when the values of `data.coder_workspace_owner.me.name` +and `data.coder_workspace.me.name` change in the above example. `name` is immutable like `user_data` above. + +You can read more about `ignore_changes` +here: https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes + +Should certain mutable attributes be required to change, you can use a more targeted approach by providing a list of +attributes to `ignore_changes`: + +```hcl +resource "docker_container" "workspace" { + lifecycle { + ignore_changes = [name] + } + + count = data.coder_workspace.me.start_count + name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" + ... +} +``` + +## Troubleshooting + +### Metrics + +- `coderd_prebuilt_workspaces_created_total` (counter): Total number of prebuilt workspaces that have been created to + meet the desired instance count of each template preset +- `coderd_prebuilt_workspaces_failed_total` (counter): Total number of prebuilt workspaces that failed to build +- `coderd_prebuilt_workspaces_claimed_total` (counter): Total number of prebuilt workspaces which were claimed by users. + Claiming refers to creating a workspace with a preset selected for which eligible prebuilt workspaces are available + and one is reassigned to a user +- `coderd_prebuilt_workspaces_desired` (gauge): Target number of prebuilt workspaces that should be available for each + template preset +- `coderd_prebuilt_workspaces_running` (gauge): Current number of prebuilt workspaces that are in a running state. These + workspaces have started successfully but may not yet be claimable by users (see `coderd_prebuilt_workspaces_eligible`) +- `coderd_prebuilt_workspaces_eligible` (gauge): Current number of prebuilt workspaces that are eligible to be claimed + by users. These are workspaces that have completed their build process with their agent reporting 'ready' status + +### Logs + +Search for `coderd.prebuilds:` to gain insight into the behaviour of the reconciliation loop diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.png b/docs/admin/templates/extending-templates/prebuilt-workspaces.png new file mode 100644 index 0000000000000000000000000000000000000000..59d11d6ed76226c7d1787f6b9d681c72737581de GIT binary patch literal 41287 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 literal 0 HcmV?d00001 diff --git a/docs/admin/templates/extending-templates/replacement-notification.png b/docs/admin/templates/extending-templates/replacement-notification.png new file mode 100644 index 0000000000000000000000000000000000000000..899c8eaf5a5ea2800129370d553e751f3239f5af GIT binary patch literal 73977 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

(jtQ2^9~%vm-y#%;fhH-`3DJ#V98 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 a;6Gyw|ITaO`?s!S00K`}KbLh*2~7YJ5VBYR literal 0 HcmV?d00001 From c806859ce83d0254255e2c1d285e84f379105175 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:20:38 +0000 Subject: [PATCH 02/17] add prebuilt to manifest; make lint/fmt/gen --- .../templates/extending-templates/prebuilt-workspaces.md | 2 +- docs/manifest.json | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 3f293aa369dd6..67c7dbc471025 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -1,4 +1,4 @@ -# Prebuilt workspaces (beta) +# Prebuilt workspaces ## Overview diff --git a/docs/manifest.json b/docs/manifest.json index ea1d19561593f..0e60376535d82 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -416,6 +416,12 @@ "description": "Use parameters to customize workspaces at build", "path": "./admin/templates/extending-templates/parameters.md" }, + { + "title": "Prebuilt workspaces", + "description": "Pre-provision a ready-to-deploy workspace with a defined set of parameters", + "path": "./admin/templates/extending-templates/prebuilt-workspaces.md", + "state": ["early access"] + }, { "title": "Icons", "description": "Customize your template with built-in icons", From 1ef926a60c9ed45703c932af1c2dba8595640ef4 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:26:06 +0000 Subject: [PATCH 03/17] relative links --- .../templates/extending-templates/prebuilt-workspaces.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 67c7dbc471025..1f4478e310610 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -10,7 +10,7 @@ one, reducing setup time significantly. - Premium license - Use `coder/coder` Terraform provider `>= 2.3.0-pre2` in your template (**TODO: update with latest version**) -- Enable the `workspace-prebuilds` [experiment](https://coder.com/docs/reference/cli/server#--experiments) +- Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments) ## Configuration @@ -66,7 +66,7 @@ startup scripts, the workspace will be marked eligible to be claimed. ## Relationship to workspace presets -[Workspace presets](https://coder.com/docs/admin/templates/extending-templates/parameters#workspace-presets-beta) allow +[Workspace presets](./parameters.md#workspace-presets-beta) allow you to configure commonly used combinations of parameters into a single option, which makes it easier for developers to pick one that fits their needs. @@ -94,7 +94,7 @@ _In future releases, we will allow operators to invalidate their prebuilt worksp ## Quotas -Prebuilt workspaces can be used in conjunction with [Resource Quotas](https://coder.com/docs/admin/users/quotas). Given +Prebuilt workspaces can be used in conjunction with [Resource Quotas](../../users/quotas.md). Given that all unclaimed prebuilt workspaces are [owned](#ownership) by the `prebuilds` user, you may configure a quota for any group which this user appears in. From 4b101f33b157000577a5b2f45fa2f4ad95a416e6 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Mon, 28 Apr 2025 21:12:49 +0000 Subject: [PATCH 04/17] copy edit --- .../prebuilt-workspaces.md | 88 +++++++++--------- .../prebuilt}/prebuilt-workspaces.png | Bin .../prebuilt}/replacement-notification.png | Bin 3 files changed, 44 insertions(+), 44 deletions(-) rename docs/{admin/templates/extending-templates => images/admin/templates/extend-templates/prebuilt}/prebuilt-workspaces.png (100%) rename docs/{admin/templates/extending-templates => images/admin/templates/extend-templates/prebuilt}/replacement-notification.png (100%) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 1f4478e310610..7a00adc1bcfb1 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -2,9 +2,9 @@ ## Overview -Prebuilt workspaces let you pre-provision and maintain a pool of ready-to-claim workspaces. -When a developer requests a workspace matching a preset, Coder assigns an existing instance instead of creating a new -one, reducing setup time significantly. +Prebuilt workspaces let you pre-provision and maintain a pool of ready-to-deploy workspaces. +Instead of creating a new workspace when a developer requests one, if a workspace matches a preset defined in the +template parameters, Coder assigns an existing instance, reducing setup time significantly. ## Prerequisites @@ -30,32 +30,34 @@ one, reducing setup time significantly. } ``` -2. Publish and import the template -3. An internal reconciliation loop maintains exactly the specified `instances` of prebuilt workspaces. + Do not define values for `coder_workspace` or `coder_workspace_owner`. + These values are [replaced](#resource-replacement) when the workspace is deployed, and if Coder detects an existing + value, it will destroy the prebuilt workspace and create a new one. -_This model of declarative configuration plus a reconciliation loop is similar to Kubernetes._ +1. Publish and import the template. +1. Coder automatically provisions another prebuilt workspace through an internal reconciliation loop + (similar to Kubernetes) to maintain the number of specified `instances`. -## Ownership +## Workspace ownership -When prebuilt workspaces are created, they are owned by the pseudo-user `prebuilds`. This user has no permissions, and -is simply a mechanism to identify unclaimed prebuilt workspaces. +After a prebuilt workspace is created, it is owned by the unprivileged pseudo-user `prebuilds`, which belongs to the +`Everyone` group. +Coder uses the `prebuilds` user to identify unclaimed prebuilt workspaces. +You can add the `prebuilds` user to additional groups if you need to. -The `prebuilds` user is as a member of the `Everyone` group, and can be added to other groups. +## View prebuilt workspaces -## Viewing prebuilt workspaces +You can view prebuilt workspaces in the **Workspaces** view in the Coder dashboard: -Given that prebuilt workspaces are just regular workspaces, you can view them in the **Workspaces** view in the -frontend: - -![prebuilt-workspaces.png](prebuilt-workspaces.png) +![A prebuilt workspace on the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) ## Claiming -A prebuilt workspace is automatically and transparently assigned to a user when the following occurs: +A prebuilt workspace is automatically and transparently assigned to a user when a: -1. The user creates a new workspace via the API or the Coder web UI -2. The user selected a preset in #1 which has been configured for prebuilds -3. A prebuilt workspace is in eligible state +- User creates a new workspace via the API or the Coder web UI. +- User selected a preset which has been configured for prebuilds. +- Prebuilt workspace is in eligible state. The ownership of the prebuilt workspace will change to the requesting user, and this is referred to as a "claim". @@ -66,26 +68,24 @@ startup scripts, the workspace will be marked eligible to be claimed. ## Relationship to workspace presets -[Workspace presets](./parameters.md#workspace-presets-beta) allow -you to configure commonly used combinations of parameters into a single option, which makes it easier for developers to -pick one that fits -their needs. +[Workspace presets](./parameters.md#workspace-presets-beta) allow you to configure commonly used combinations of +parameters into a single option, which makes it easier for developers to pick one that fits their needs. -Prebuilt workspaces need to have a preset defined to match the _base configuration_ of a workspace, i.e. the preset +Prebuilt workspaces need to have a preset defined to match the base configuration of a workspace, i.e. the preset needs to define all the required parameters needed to build a workspace. These parameters are necessary in order to build workspaces in the background. -Parameters which are not required or not part of a preset can still be used with prebuilt workspaces. The preset defines -the minimum required set of parameters, and these are immutable. +Parameters which are not required or not part of a preset can still be used with prebuilt workspaces. +The preset defines the minimum required set of parameters, and these are immutable. -## Invalidation +## Update a prebuilt workspace -Prebuilt workspaces are _never_ updated after they are created. +Prebuilt workspaces are not automatically updated after they are created. -Whenever a template version changes, all prebuilt workspaces relating to an inactive template version will be destroyed. +When a template version changes, all prebuilt workspaces relating to an inactive template version are destroyed. New prebuilt workspaces will be provisioned for the active template version. -Invalidating prebuilt workspaces is useful when your template version does not change but a referenced dependency does, +You can invalidate a prebuilt workspace your template version does not change but a referenced dependency does, which is necessary for running an up-to-date workspace. For example, if an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) which is referenced by your template is updated, you can simply delete the prebuilt workspaces, and they will be recreated with the latest AMI. @@ -95,32 +95,33 @@ _In future releases, we will allow operators to invalidate their prebuilt worksp ## Quotas Prebuilt workspaces can be used in conjunction with [Resource Quotas](../../users/quotas.md). Given -that all unclaimed prebuilt workspaces are [owned](#ownership) by the `prebuilds` user, you may configure a quota for +that all unclaimed prebuilt workspaces are [owned](#workspace-ownership) by the `prebuilds` user, you may configure a quota for any group which this user appears in. Once the quota is exceeded, prebuilt workspaces will fail provisioning like regular workspaces would. ## Current Limitations -### Organizations +- Organizations -Prebuilt workspaces can only be utilized by the default organization. + Prebuilt workspaces can only be utilized by the default organization. -https://github.com/coder/internal/issues/364 is open to track this feature, and will be implemented in a future release. + [coder/internal#364](https://github.com/coder/internal/issues/364) -### Autoscaling +- Autoscaling -Prebuilt workspaces will remain running indefinitely until they are claimed. We do not at present have an autoscaling -mechanism to reduce the number of instances after working hours. + Prebuilt workspaces remain running until they are claimed. + We do not currently have an autoscaling mechanism to reduce the number of instances after working hours. -https://github.com/coder/internal/issues/312 is open to track this feature, and will be implemented in a future release. + [coder/internal#312](https://github.com/coder/internal/issues/312) ## Gotchas ### Resource Replacement -When a prebuilt workspace is created, it is initially [owned](#ownership) by the `prebuilds` user and a random name -is generated for it. When `terraform apply` runs, it will provide these values during provisioning in the +When a prebuilt workspace is created, it is initially [owned](#workspace-ownership) by the `prebuilds` user and a random name +is generated for it. +When `terraform apply` runs, it will provide these values during provisioning in the [`coder_workspace`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace) and [`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner) datasources. @@ -139,7 +140,7 @@ eliminating the value of the prior pre-provisioning. Should this occur when a prebuilt workspace is claimed, all Template Admins will receive a notification which will link them to the build logs to investigate which resource was being replaced. -![replacement-notification.png](replacement-notification.png) +![Workspace replaced notification.png](../../../images/admin/templates/extend-templates/prebuilt/replacement-notification.png) To avoid this problem, you will need to add a `lifecycle` block to your resource: @@ -159,8 +160,7 @@ In the above example, the `docker_container` would be created with a `name` attr initial owner (i.e. `prebuilds`), and will never change - even when the values of `data.coder_workspace_owner.me.name` and `data.coder_workspace.me.name` change in the above example. `name` is immutable like `user_data` above. -You can read more about `ignore_changes` -here: https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes +You can read more about `ignore_changes`in the [Terraform documentation](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes). Should certain mutable attributes be required to change, you can use a more targeted approach by providing a list of attributes to `ignore_changes`: @@ -196,4 +196,4 @@ resource "docker_container" "workspace" { ### Logs -Search for `coderd.prebuilds:` to gain insight into the behaviour of the reconciliation loop +Search for `coderd.prebuilds:` to gain insight into the behavior of the reconciliation loop. diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.png b/docs/images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png similarity index 100% rename from docs/admin/templates/extending-templates/prebuilt-workspaces.png rename to docs/images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png diff --git a/docs/admin/templates/extending-templates/replacement-notification.png b/docs/images/admin/templates/extend-templates/prebuilt/replacement-notification.png similarity index 100% rename from docs/admin/templates/extending-templates/replacement-notification.png rename to docs/images/admin/templates/extend-templates/prebuilt/replacement-notification.png From 1c6aee4db77c17e8baf75542776465451b0357b8 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Tue, 29 Apr 2025 09:52:08 +0200 Subject: [PATCH 05/17] chore: review comments & additions Signed-off-by: Danny Kopping --- .../extending-templates/prebuilt-workspaces.md | 14 +++++--------- docs/manifest.json | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 7a00adc1bcfb1..352cbee4c2abc 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -2,7 +2,7 @@ ## Overview -Prebuilt workspaces let you pre-provision and maintain a pool of ready-to-deploy workspaces. +Prebuilt workspaces let you pre-provision and maintain a pool of ready-to-use workspaces. Instead of creating a new workspace when a developer requests one, if a workspace matches a preset defined in the template parameters, Coder assigns an existing instance, reducing setup time significantly. @@ -30,10 +30,6 @@ template parameters, Coder assigns an existing instance, reducing setup time sig } ``` - Do not define values for `coder_workspace` or `coder_workspace_owner`. - These values are [replaced](#resource-replacement) when the workspace is deployed, and if Coder detects an existing - value, it will destroy the prebuilt workspace and create a new one. - 1. Publish and import the template. 1. Coder automatically provisions another prebuilt workspace through an internal reconciliation loop (similar to Kubernetes) to maintain the number of specified `instances`. @@ -49,7 +45,7 @@ You can add the `prebuilds` user to additional groups if you need to. You can view prebuilt workspaces in the **Workspaces** view in the Coder dashboard: -![A prebuilt workspace on the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) +![A prebuilt workspace in the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) ## Claiming @@ -57,7 +53,7 @@ A prebuilt workspace is automatically and transparently assigned to a user when - User creates a new workspace via the API or the Coder web UI. - User selected a preset which has been configured for prebuilds. -- Prebuilt workspace is in eligible state. +- Prebuilt workspace is in an eligible state. The ownership of the prebuilt workspace will change to the requesting user, and this is referred to as a "claim". @@ -85,7 +81,7 @@ Prebuilt workspaces are not automatically updated after they are created. When a template version changes, all prebuilt workspaces relating to an inactive template version are destroyed. New prebuilt workspaces will be provisioned for the active template version. -You can invalidate a prebuilt workspace your template version does not change but a referenced dependency does, +You may want to invalidate a prebuilt workspace when your template version does not change but a referenced dependency does, which is necessary for running an up-to-date workspace. For example, if an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) which is referenced by your template is updated, you can simply delete the prebuilt workspaces, and they will be recreated with the latest AMI. @@ -140,7 +136,7 @@ eliminating the value of the prior pre-provisioning. Should this occur when a prebuilt workspace is claimed, all Template Admins will receive a notification which will link them to the build logs to investigate which resource was being replaced. -![Workspace replaced notification.png](../../../images/admin/templates/extend-templates/prebuilt/replacement-notification.png) +![Resource replacement notification](../../../images/admin/templates/extend-templates/prebuilt/replacement-notification.png) To avoid this problem, you will need to add a `lifecycle` block to your resource: diff --git a/docs/manifest.json b/docs/manifest.json index 0e60376535d82..d3a8c64ad2562 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -420,7 +420,7 @@ "title": "Prebuilt workspaces", "description": "Pre-provision a ready-to-deploy workspace with a defined set of parameters", "path": "./admin/templates/extending-templates/prebuilt-workspaces.md", - "state": ["early access"] + "state": ["premium", "beta"] }, { "title": "Icons", From 5c603db09645d5a3f81bd9413df7222b4965e4f1 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Tue, 29 Apr 2025 19:13:49 +0000 Subject: [PATCH 06/17] copy edit --- .../prebuilt-workspaces.md | 229 ++++++++++-------- 1 file changed, 126 insertions(+), 103 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 352cbee4c2abc..9e798b299a302 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -1,20 +1,29 @@ # Prebuilt workspaces -## Overview +Prebuilt workspaces (Prebuilds) allow template administrators to improve the developer experience by reducing workspace +creation time with an automatically maintained pool of ready-to-use workspaces for specific parameter presets. -Prebuilt workspaces let you pre-provision and maintain a pool of ready-to-use workspaces. -Instead of creating a new workspace when a developer requests one, if a workspace matches a preset defined in the -template parameters, Coder assigns an existing instance, reducing setup time significantly. +The template administrator configures a template to be available with prebuilt workspaces, and then when a developer creates +a new workspace with matching parameters, Coder assigns them an existing prebuilt instance. +Prebuilt workspaces significantly reduce wait times, especially for templates with complex provisioning or lengthy startup procedures. + +Prebuilt workspaces are: + +- Created and maintained automatically by Coder to match your specified preset configurations. +- Claimed transparently when developers request matching workspaces. +- Monitored and replaced automatically to maintain your desired pool size. ## Prerequisites -- Premium license -- Use `coder/coder` Terraform provider `>= 2.3.0-pre2` in your template (**TODO: update with latest version**) -- Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments) +- [**Premium license**](../../licensing/index.md) +- **Template administrator privileges**: User must have permissions to create or modify templates. +- **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.3.0-pre2`. (**TODO: update with latest version**) +- **Feature flag**: Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments). -## Configuration +## Enable Prebuilds for template presets -1. In your Terraform template, add a `prebuilds` block within a `coder_workspace_preset` block: +In your template, add a `prebuilds` block within a `coder_workspace_preset` block to identify how many prebuilt +instances your Coder deployment should maintain: ```hcl data "coder_workspace_preset" "goland" { @@ -25,120 +34,120 @@ template parameters, Coder assigns an existing instance, reducing setup time sig memory = 16 } prebuilds { - instances = 3 + instances = 3 // Number of prebuilt workspaces to maintain } } ``` -1. Publish and import the template. -1. Coder automatically provisions another prebuilt workspace through an internal reconciliation loop - (similar to Kubernetes) to maintain the number of specified `instances`. +After you create or update the template, Coder automatically provisions and maintains prebuilt workspaces through an +internal reconciliation loop (similar to Kubernetes) to maintain the number of specified `instances`. -## Workspace ownership +Prebuilt workspaces are displayed in the list of workspaces on the Coder dashboard with the owner set to `prebuilds`. -After a prebuilt workspace is created, it is owned by the unprivileged pseudo-user `prebuilds`, which belongs to the -`Everyone` group. -Coder uses the `prebuilds` user to identify unclaimed prebuilt workspaces. -You can add the `prebuilds` user to additional groups if you need to. +## Prebuild lifecycle -## View prebuilt workspaces +Prebuilt workspaces follow a specific lifecycle from creation through eligibility to claiming. -You can view prebuilt workspaces in the **Workspaces** view in the Coder dashboard: +Expand each item in this list for more information about the stage: -![A prebuilt workspace in the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) +1.
Prebuilds provisioning and initial state + + After you configure a preset with Prebuilds and publish the template: + + 1. Coder automatically creates prebuilt workspaces up to the specified `instances` count. + 1. Each new prebuild is initially owned by an unprivileged system pseudo-user named `prebuilds`. + - The `prebuilds` user belongs to the `Everyone` group (you can add it to additional groups if needed). + 1. Each Prebuild receives a randomly generated name for identification. -## Claiming +
-A prebuilt workspace is automatically and transparently assigned to a user when a: +1.
Eligibility process -- User creates a new workspace via the API or the Coder web UI. -- User selected a preset which has been configured for prebuilds. -- Prebuilt workspace is in an eligible state. + Before a prebuilt workspace is available to users: -The ownership of the prebuilt workspace will change to the requesting user, and this is referred to as a "claim". + 1. The workspace is provisioned like a regular workspace. + 1. The workspace reaches `running` state. + 1. The agent connects and reports `ready` status. + 1. All bootstrap procedures and startup scripts complete successfully. + 1. The workspace is marked as `eligible` to be claimed. -## Eligibility + Prebuilds that fail during provisioning are retried with an exponential backoff to prevent resource waste. -When a prebuilt workspace is running, and its agent has completed all of its bootstrap procedures and executed its -startup scripts, the workspace will be marked eligible to be claimed. +
-## Relationship to workspace presets +1.
Claiming process -[Workspace presets](./parameters.md#workspace-presets-beta) allow you to configure commonly used combinations of -parameters into a single option, which makes it easier for developers to pick one that fits their needs. + When a developer requests a new workspace, the claiming process occurs: -Prebuilt workspaces need to have a preset defined to match the base configuration of a workspace, i.e. the preset -needs to define all the required parameters needed to build a workspace. These parameters are necessary in order to -build workspaces in the background. + 1. Developer selects a template and preset that has Prebuilds configured. + 1. If an eligible prebuilt workspace exists, it's automatically assigned to the user. + 1. Ownership transfers from the `prebuilds` user to the requesting user. + 1. The workspace name changes to the user's requested name. + 1. The process is transparent to the developer - they simply see a workspace ready faster than normal. -Parameters which are not required or not part of a preset can still be used with prebuilt workspaces. -The preset defines the minimum required set of parameters, and these are immutable. + This ownership transfer happens automatically without any special action required by the user. -## Update a prebuilt workspace +
-Prebuilt workspaces are not automatically updated after they are created. +You can view available prebuilt workspaces in the **Workspaces** view in the Coder dashboard: -When a template version changes, all prebuilt workspaces relating to an inactive template version are destroyed. -New prebuilt workspaces will be provisioned for the active template version. +![A prebuilt workspace in the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) -You may want to invalidate a prebuilt workspace when your template version does not change but a referenced dependency does, -which is necessary for running an up-to-date workspace. For example, if -an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) which is referenced by your template is updated, -you can simply delete the prebuilt workspaces, and they will be recreated with the latest AMI. +### Template updates and prebuilt lifecycle -_In future releases, we will allow operators to invalidate their prebuilt workspaces programmatically._ +Prebuilt workspaces have specific behavior when templates are updated: -## Quotas +1. When a template version changes, Prebuilds for old versions are automatically deleted. +1. New Prebuilds are created for the active template version. +1. Prebuilds aren't automatically updated after creation. +1. If dependencies change (e.g., an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) update) without a template version change, you can: + - Delete the existing Prebuilds manually. + - Coder will automatically create new prebuilt workspaces with the updated dependencies. -Prebuilt workspaces can be used in conjunction with [Resource Quotas](../../users/quotas.md). Given -that all unclaimed prebuilt workspaces are [owned](#workspace-ownership) by the `prebuilds` user, you may configure a quota for -any group which this user appears in. +The system always maintains the desired number of prebuilt workspaces for the active template version. -Once the quota is exceeded, prebuilt workspaces will fail provisioning like regular workspaces would. +### Relationship to workspace presets -## Current Limitations +Prebuilt workspaces are tightly integrated with [workspace presets](./parameters.md#workspace-presets-beta): -- Organizations +1. Each Prebuild is associated with a specific template preset. +1. The preset must define all required parameters needed to build the workspace. +1. Parameters that are not defined in the preset can still be customized by users when they claim a workspace. +1. The preset parameters define the base configuration and are immutable after they're claimed. +1. Prebuilds help presets deliver even faster workspace creation. - Prebuilt workspaces can only be utilized by the default organization. +_Note: In future releases, we will allow operators to invalidate their prebuilt workspaces programmatically._ - [coder/internal#364](https://github.com/coder/internal/issues/364) +## Administration and troubleshooting -- Autoscaling +### Managing resource quotas - Prebuilt workspaces remain running until they are claimed. - We do not currently have an autoscaling mechanism to reduce the number of instances after working hours. +Prebuilt workspaces can be used in conjunction with [resource quotas](../../users/quotas.md). +Because unclaimed prebuilt workspaces are owned by the `prebuilds` user, you can: - [coder/internal#312](https://github.com/coder/internal/issues/312) +1. Configure quotas for any group that includes this user. +1. Set appropriate limits to balance Prebuild availability with resource constraints. +1. Monitor quota utilization through Coder's dashboard. -## Gotchas +If a quota is exceeded, the prebuilt workspace will fail provisioning the same way other workspaces do. -### Resource Replacement +### Template configuration best practices -When a prebuilt workspace is created, it is initially [owned](#workspace-ownership) by the `prebuilds` user and a random name -is generated for it. -When `terraform apply` runs, it will provide these values during provisioning in the -[`coder_workspace`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace) and -[`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner) -datasources. +#### Preventing resource replacement -Once a prebuilt workspace is claimed, the ownership of that workspace changes to the requesting user and -`terraform apply` is run again, now with updated values for the aforementioned datasources. +When a prebuilt workspace is claimed, Terraform runs again with new values for the workspace owner and name. -If your template has used these datasources in immutable fields (i.e. the -[`user_data`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#user_data-1) field in -an `aws_instance` resource), Terraform will interpret these changes as _drift_ and will therefore destroy and recreate -the resource. +This can cause issues: -This is obviously undesirable because the prebuilt workspace will now have to provision _again_, while the user waits, -eliminating the value of the prior pre-provisioning. +1. The workspace is initially created with values from the `prebuilds` user and a random name. +1. After claiming, the workspace owner and name change, which Terraform sees as configuration drift. +1. If these values are used in immutable fields, Terraform will destroy and recreate the resource, eliminating the benefit of prebuilds. -Should this occur when a prebuilt workspace is claimed, all Template Admins will receive a notification which will -link them to the build logs to investigate which resource was being replaced. +For example, when these values are used in immutable fields like the AWS instance `user_data`, you'll see resource replacement during claiming: ![Resource replacement notification](../../../images/admin/templates/extend-templates/prebuilt/replacement-notification.png) -To avoid this problem, you will need to add a `lifecycle` block to your resource: +To prevent this, add a `lifecycle` block with `ignore_changes`: ```hcl resource "docker_container" "workspace" { @@ -152,14 +161,7 @@ resource "docker_container" "workspace" { } ``` -In the above example, the `docker_container` would be created with a `name` attribute which includes references to the -initial owner (i.e. `prebuilds`), and will never change - even when the values of `data.coder_workspace_owner.me.name` -and `data.coder_workspace.me.name` change in the above example. `name` is immutable like `user_data` above. - -You can read more about `ignore_changes`in the [Terraform documentation](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes). - -Should certain mutable attributes be required to change, you can use a more targeted approach by providing a list of -attributes to `ignore_changes`: +For more targeted control, specify which attributes to ignore: ```hcl resource "docker_container" "workspace" { @@ -173,23 +175,44 @@ resource "docker_container" "workspace" { } ``` -## Troubleshooting +Learn more about `ignore_changes` in the [Terraform documentation](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes). + +### Current limitations + +The prebuilt workspaces feature has these current limitations: + +- **Organizations** + + Prebuilt workspaces can only be used with the default organization. + + [coder/internal#364](https://github.com/coder/internal/issues/364) + +- **Autoscaling** + + Prebuilt workspaces remain running until claimed. There's no automated mechanism to reduce instances during off-hours. + + [coder/internal#312](https://github.com/coder/internal/issues/312) + +### Monitoring and observability + +#### Available metrics + +Coder provides several metrics to monitor your prebuilt workspaces: + +- `coderd_prebuilt_workspaces_created_total` (counter): Total number of prebuilt workspaces created to meet the desired instance count. +- `coderd_prebuilt_workspaces_failed_total` (counter): Total number of prebuilt workspaces that failed to build. +- `coderd_prebuilt_workspaces_claimed_total` (counter): Total number of prebuilt workspaces claimed by users. +- `coderd_prebuilt_workspaces_desired` (gauge): Target number of prebuilt workspaces that should be available. +- `coderd_prebuilt_workspaces_running` (gauge): Current number of prebuilt workspaces in running state. +- `coderd_prebuilt_workspaces_eligible` (gauge): Current number of prebuilt workspaces eligible to be claimed. -### Metrics +#### Logs -- `coderd_prebuilt_workspaces_created_total` (counter): Total number of prebuilt workspaces that have been created to - meet the desired instance count of each template preset -- `coderd_prebuilt_workspaces_failed_total` (counter): Total number of prebuilt workspaces that failed to build -- `coderd_prebuilt_workspaces_claimed_total` (counter): Total number of prebuilt workspaces which were claimed by users. - Claiming refers to creating a workspace with a preset selected for which eligible prebuilt workspaces are available - and one is reassigned to a user -- `coderd_prebuilt_workspaces_desired` (gauge): Target number of prebuilt workspaces that should be available for each - template preset -- `coderd_prebuilt_workspaces_running` (gauge): Current number of prebuilt workspaces that are in a running state. These - workspaces have started successfully but may not yet be claimable by users (see `coderd_prebuilt_workspaces_eligible`) -- `coderd_prebuilt_workspaces_eligible` (gauge): Current number of prebuilt workspaces that are eligible to be claimed - by users. These are workspaces that have completed their build process with their agent reporting 'ready' status +Search for `coderd.prebuilds:` in your logs to track the reconciliation loop's behavior. -### Logs +These logs provide information about: -Search for `coderd.prebuilds:` to gain insight into the behavior of the reconciliation loop. +1. Creation and deletion attempts for prebuilds. +1. Backoff events after failed builds. +1. Eligibility state changes. +1. Claiming operations. From 4b3b0a9d86a84259d0c19d0e14ad2478f8598b28 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Tue, 29 Apr 2025 19:18:06 +0000 Subject: [PATCH 07/17] link to groups roles --- .../templates/extending-templates/prebuilt-workspaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 9e798b299a302..b208101168124 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -16,7 +16,7 @@ Prebuilt workspaces are: ## Prerequisites - [**Premium license**](../../licensing/index.md) -- **Template administrator privileges**: User must have permissions to create or modify templates. +- [**Template administrator privileges**](../../users/groups-roles.md) - **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.3.0-pre2`. (**TODO: update with latest version**) - **Feature flag**: Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments). @@ -203,7 +203,7 @@ Coder provides several metrics to monitor your prebuilt workspaces: - `coderd_prebuilt_workspaces_failed_total` (counter): Total number of prebuilt workspaces that failed to build. - `coderd_prebuilt_workspaces_claimed_total` (counter): Total number of prebuilt workspaces claimed by users. - `coderd_prebuilt_workspaces_desired` (gauge): Target number of prebuilt workspaces that should be available. -- `coderd_prebuilt_workspaces_running` (gauge): Current number of prebuilt workspaces in running state. +- `coderd_prebuilt_workspaces_running` (gauge): Current number of prebuilt workspaces in a `running` state. - `coderd_prebuilt_workspaces_eligible` (gauge): Current number of prebuilt workspaces eligible to be claimed. #### Logs From 684ef43e570be299cc99d7d8703d5c046bffa64c Mon Sep 17 00:00:00 2001 From: Edward Angert Date: Tue, 29 Apr 2025 17:00:42 -0400 Subject: [PATCH 08/17] Apply suggestions from code review Co-authored-by: Danny Kopping --- .../prebuilt-workspaces.md | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index b208101168124..a97ac6f9b6255 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -1,22 +1,21 @@ # Prebuilt workspaces -Prebuilt workspaces (Prebuilds) allow template administrators to improve the developer experience by reducing workspace +Prebuilt workspaces allow template administrators to improve the developer experience by reducing workspace creation time with an automatically maintained pool of ready-to-use workspaces for specific parameter presets. -The template administrator configures a template to be available with prebuilt workspaces, and then when a developer creates +The template administrator configures a template to provision prebuilt workspaces in the background, and then when a developer creates a new workspace with matching parameters, Coder assigns them an existing prebuilt instance. Prebuilt workspaces significantly reduce wait times, especially for templates with complex provisioning or lengthy startup procedures. Prebuilt workspaces are: - Created and maintained automatically by Coder to match your specified preset configurations. -- Claimed transparently when developers request matching workspaces. +- Claimed transparently when developers create workspaces. - Monitored and replaced automatically to maintain your desired pool size. ## Prerequisites - [**Premium license**](../../licensing/index.md) -- [**Template administrator privileges**](../../users/groups-roles.md) - **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.3.0-pre2`. (**TODO: update with latest version**) - **Feature flag**: Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments). @@ -34,7 +33,7 @@ instances your Coder deployment should maintain: memory = 16 } prebuilds { - instances = 3 // Number of prebuilt workspaces to maintain + instances = 3 # Number of prebuilt workspaces to maintain } } ``` @@ -67,11 +66,11 @@ Expand each item in this list for more information about the stage: 1. The workspace is provisioned like a regular workspace. 1. The workspace reaches `running` state. - 1. The agent connects and reports `ready` status. - 1. All bootstrap procedures and startup scripts complete successfully. - 1. The workspace is marked as `eligible` to be claimed. + 1. The agent connects to coderd. + 1. The agent starts its bootstrap procedures and startup scripts complete successfully. + 1. The agent reports `ready` status. - Prebuilds that fail during provisioning are retried with an exponential backoff to prevent resource waste. + Prebuilds that fail during provisioning are retried with an exponential backoff to prevent transient failures. @@ -112,11 +111,9 @@ Prebuilt workspaces are tightly integrated with [workspace presets](./parameters 1. Each Prebuild is associated with a specific template preset. 1. The preset must define all required parameters needed to build the workspace. -1. Parameters that are not defined in the preset can still be customized by users when they claim a workspace. 1. The preset parameters define the base configuration and are immutable after they're claimed. -1. Prebuilds help presets deliver even faster workspace creation. +1. Parameters that are not defined in the preset can still be customized by users when they claim a workspace. -_Note: In future releases, we will allow operators to invalidate their prebuilt workspaces programmatically._ ## Administration and troubleshooting @@ -127,7 +124,6 @@ Because unclaimed prebuilt workspaces are owned by the `prebuilds` user, you can 1. Configure quotas for any group that includes this user. 1. Set appropriate limits to balance Prebuild availability with resource constraints. -1. Monitor quota utilization through Coder's dashboard. If a quota is exceeded, the prebuilt workspace will fail provisioning the same way other workspaces do. @@ -135,7 +131,7 @@ If a quota is exceeded, the prebuilt workspace will fail provisioning the same w #### Preventing resource replacement -When a prebuilt workspace is claimed, Terraform runs again with new values for the workspace owner and name. +When a prebuilt workspace is claimed, another `terraform apply` run occurs with new values for the workspace owner and name. This can cause issues: @@ -214,5 +210,4 @@ These logs provide information about: 1. Creation and deletion attempts for prebuilds. 1. Backoff events after failed builds. -1. Eligibility state changes. 1. Claiming operations. From a10402f4df9ec900cda513b0e7e20ebc70b8a879 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Tue, 29 Apr 2025 21:16:04 +0000 Subject: [PATCH 09/17] s/prebuilds/prebuilt workspaces --- .../prebuilt-workspaces.md | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index a97ac6f9b6255..603b19cec32e1 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -19,7 +19,7 @@ Prebuilt workspaces are: - **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.3.0-pre2`. (**TODO: update with latest version**) - **Feature flag**: Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments). -## Enable Prebuilds for template presets +## Enable prebuilt workspaces for template presets In your template, add a `prebuilds` block within a `coder_workspace_preset` block to identify how many prebuilt instances your Coder deployment should maintain: @@ -43,20 +43,21 @@ internal reconciliation loop (similar to Kubernetes) to maintain the number of s Prebuilt workspaces are displayed in the list of workspaces on the Coder dashboard with the owner set to `prebuilds`. -## Prebuild lifecycle +## Prebuilt workspace lifecycle Prebuilt workspaces follow a specific lifecycle from creation through eligibility to claiming. Expand each item in this list for more information about the stage: -1.
Prebuilds provisioning and initial state +1.
The prebuilt workspace is provisioned. - After you configure a preset with Prebuilds and publish the template: + After you configure a preset with a prebuilt workspace and publish the template: 1. Coder automatically creates prebuilt workspaces up to the specified `instances` count. 1. Each new prebuild is initially owned by an unprivileged system pseudo-user named `prebuilds`. - The `prebuilds` user belongs to the `Everyone` group (you can add it to additional groups if needed). - 1. Each Prebuild receives a randomly generated name for identification. + 1. Each prebuilt workspace receives a randomly generated name for identification. + 1. The workspace is provisioned like a regular workspace.
@@ -64,13 +65,12 @@ Expand each item in this list for more information about the stage: Before a prebuilt workspace is available to users: - 1. The workspace is provisioned like a regular workspace. 1. The workspace reaches `running` state. 1. The agent connects to coderd. 1. The agent starts its bootstrap procedures and startup scripts complete successfully. 1. The agent reports `ready` status. - Prebuilds that fail during provisioning are retried with an exponential backoff to prevent transient failures. + Prebuilt workspaces that fail during provisioning are retried with an exponential backoff to prevent transient failures.
@@ -78,7 +78,7 @@ Expand each item in this list for more information about the stage: When a developer requests a new workspace, the claiming process occurs: - 1. Developer selects a template and preset that has Prebuilds configured. + 1. Developer selects a template and preset that has prebuilt workspaces configured. 1. If an eligible prebuilt workspace exists, it's automatically assigned to the user. 1. Ownership transfers from the `prebuilds` user to the requesting user. 1. The workspace name changes to the user's requested name. @@ -96,11 +96,11 @@ You can view available prebuilt workspaces in the **Workspaces** view in the Cod Prebuilt workspaces have specific behavior when templates are updated: -1. When a template version changes, Prebuilds for old versions are automatically deleted. -1. New Prebuilds are created for the active template version. -1. Prebuilds aren't automatically updated after creation. +1. When a template version changes, prebuilt workspaces for old versions are automatically deleted. +1. New prebuilt workspaces are created for the active template version. +1. Prebuilt workspaces aren't automatically updated after creation. 1. If dependencies change (e.g., an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) update) without a template version change, you can: - - Delete the existing Prebuilds manually. + - Delete the existing prebuilt workspaces manually. - Coder will automatically create new prebuilt workspaces with the updated dependencies. The system always maintains the desired number of prebuilt workspaces for the active template version. @@ -109,12 +109,11 @@ The system always maintains the desired number of prebuilt workspaces for the ac Prebuilt workspaces are tightly integrated with [workspace presets](./parameters.md#workspace-presets-beta): -1. Each Prebuild is associated with a specific template preset. +1. Each prebuilt workspace is associated with a specific template preset. 1. The preset must define all required parameters needed to build the workspace. 1. The preset parameters define the base configuration and are immutable after they're claimed. 1. Parameters that are not defined in the preset can still be customized by users when they claim a workspace. - ## Administration and troubleshooting ### Managing resource quotas @@ -123,7 +122,7 @@ Prebuilt workspaces can be used in conjunction with [resource quotas](../../user Because unclaimed prebuilt workspaces are owned by the `prebuilds` user, you can: 1. Configure quotas for any group that includes this user. -1. Set appropriate limits to balance Prebuild availability with resource constraints. +1. Set appropriate limits to balance prebuilt workspace availability with resource constraints. If a quota is exceeded, the prebuilt workspace will fail provisioning the same way other workspaces do. @@ -208,6 +207,6 @@ Search for `coderd.prebuilds:` in your logs to track the reconciliation loop's b These logs provide information about: -1. Creation and deletion attempts for prebuilds. +1. Creation and deletion attempts for prebuilt workspaces. 1. Backoff events after failed builds. 1. Claiming operations. From 850581e89918eeb36677c60876a79c5024ec634b Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Tue, 29 Apr 2025 21:17:00 +0000 Subject: [PATCH 10/17] heading edit --- docs/admin/templates/extending-templates/prebuilt-workspaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 603b19cec32e1..571fe62d66d70 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -92,7 +92,7 @@ You can view available prebuilt workspaces in the **Workspaces** view in the Cod ![A prebuilt workspace in the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) -### Template updates and prebuilt lifecycle +### Template updates and the prebuilt workspace lifecycle Prebuilt workspaces have specific behavior when templates are updated: From 88f918ab44e73ff8b7aba967da119b96202abbf3 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Wed, 30 Apr 2025 20:50:06 +0000 Subject: [PATCH 11/17] edit prebuilt workspace lifecycle steps --- .../extending-templates/prebuilt-workspaces.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 571fe62d66d70..0fedc236fd4c5 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -49,9 +49,8 @@ Prebuilt workspaces follow a specific lifecycle from creation through eligibilit Expand each item in this list for more information about the stage: -1.
The prebuilt workspace is provisioned. - - After you configure a preset with a prebuilt workspace and publish the template: +1.
After you configure a preset with a prebuilt workspace and publish the template, Coder provisions + the prebuilt workspace(s). 1. Coder automatically creates prebuilt workspaces up to the specified `instances` count. 1. Each new prebuild is initially owned by an unprivileged system pseudo-user named `prebuilds`. @@ -61,7 +60,7 @@ Expand each item in this list for more information about the stage:
-1.
Eligibility process +1.
Coder prepares the prebuilt workspace to be claimed by a developer. Before a prebuilt workspace is available to users: @@ -74,9 +73,7 @@ Expand each item in this list for more information about the stage:
-1.
Claiming process - - When a developer requests a new workspace, the claiming process occurs: +1.
When a developer selects requests a new workspace, the claiming process occurs: 1. Developer selects a template and preset that has prebuilt workspaces configured. 1. If an eligible prebuilt workspace exists, it's automatically assigned to the user. From d95806b99e57fe2368a338ee8a6da324679133bd Mon Sep 17 00:00:00 2001 From: Edward Angert Date: Mon, 5 May 2025 14:30:21 -0400 Subject: [PATCH 12/17] Apply suggestions from code review --- docs/admin/templates/extending-templates/prebuilt-workspaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 0fedc236fd4c5..659946f787dc6 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -4,7 +4,7 @@ Prebuilt workspaces allow template administrators to improve the developer exper creation time with an automatically maintained pool of ready-to-use workspaces for specific parameter presets. The template administrator configures a template to provision prebuilt workspaces in the background, and then when a developer creates -a new workspace with matching parameters, Coder assigns them an existing prebuilt instance. +a new workspace that matches the preset, Coder assigns them an existing prebuilt instance. Prebuilt workspaces significantly reduce wait times, especially for templates with complex provisioning or lengthy startup procedures. Prebuilt workspaces are: From 6439616b7f3222435c74c4159f1c0067bb017ce8 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Mon, 5 May 2025 19:00:40 +0000 Subject: [PATCH 13/17] less-specific resource replacement --- docs/admin/templates/extending-templates/prebuilt-workspaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 659946f787dc6..e0f5778819018 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -132,7 +132,7 @@ When a prebuilt workspace is claimed, another `terraform apply` run occurs with This can cause issues: 1. The workspace is initially created with values from the `prebuilds` user and a random name. -1. After claiming, the workspace owner and name change, which Terraform sees as configuration drift. +1. After claiming, various workspace properties change (ownership, name, and potentially other values), which Terraform sees as configuration drift. 1. If these values are used in immutable fields, Terraform will destroy and recreate the resource, eliminating the benefit of prebuilds. For example, when these values are used in immutable fields like the AWS instance `user_data`, you'll see resource replacement during claiming: From dfb7958f5e10226d57eba8d2fc160a29ff5cd257 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Tue, 6 May 2025 10:43:07 +0200 Subject: [PATCH 14/17] fix: copy updates Signed-off-by: Danny Kopping --- .../prebuilt-workspaces.md | 79 +++++++++---------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index e0f5778819018..2ea737e3f69d0 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -13,6 +13,15 @@ Prebuilt workspaces are: - Claimed transparently when developers create workspaces. - Monitored and replaced automatically to maintain your desired pool size. +## Relationship to workspace presets + +Prebuilt workspaces are tightly integrated with [workspace presets](./parameters.md#workspace-presets-beta): + +1. Each prebuilt workspace is associated with a specific template preset. +1. The preset must define all required parameters needed to build the workspace. +1. The preset parameters define the base configuration and are immutable once a prebuilt workspace is provisioned. +1. Parameters that are not defined in the preset can still be customized by users when they claim a workspace. + ## Prerequisites - [**Premium license**](../../licensing/index.md) @@ -21,7 +30,7 @@ Prebuilt workspaces are: ## Enable prebuilt workspaces for template presets -In your template, add a `prebuilds` block within a `coder_workspace_preset` block to identify how many prebuilt +In your template, add a `prebuilds` block within a `coder_workspace_preset` definition to identify how many prebuilt instances your Coder deployment should maintain: ```hcl @@ -38,79 +47,65 @@ instances your Coder deployment should maintain: } ``` -After you create or update the template, Coder automatically provisions and maintains prebuilt workspaces through an -internal reconciliation loop (similar to Kubernetes) to maintain the number of specified `instances`. - -Prebuilt workspaces are displayed in the list of workspaces on the Coder dashboard with the owner set to `prebuilds`. +After you publish a new template version, Coder will automatically provision and maintain prebuilt workspaces through an +internal reconciliation loop (similar to Kubernetes) to ensure the defined `instances` count are running. ## Prebuilt workspace lifecycle Prebuilt workspaces follow a specific lifecycle from creation through eligibility to claiming. -Expand each item in this list for more information about the stage: +1. After you configure a preset with prebuilds and publish the template, Coder provisions the prebuilt workspace(s). -1.
After you configure a preset with a prebuilt workspace and publish the template, Coder provisions - the prebuilt workspace(s). - - 1. Coder automatically creates prebuilt workspaces up to the specified `instances` count. - 1. Each new prebuild is initially owned by an unprivileged system pseudo-user named `prebuilds`. + 1. Coder automatically creates the defined `instances` count of prebuilt workspaces. + 1. Each new prebuilt workspace is initially owned by an unprivileged system pseudo-user named `prebuilds`. - The `prebuilds` user belongs to the `Everyone` group (you can add it to additional groups if needed). 1. Each prebuilt workspace receives a randomly generated name for identification. - 1. The workspace is provisioned like a regular workspace. - -
+ 1. The workspace is provisioned like a regular workspace; only its ownership distinguishes it as a prebuilt workspace. -1.
Coder prepares the prebuilt workspace to be claimed by a developer. +1. Prebuilt workspaces start up and become eligible to be claimed by a developer. Before a prebuilt workspace is available to users: - 1. The workspace reaches `running` state. - 1. The agent connects to coderd. - 1. The agent starts its bootstrap procedures and startup scripts complete successfully. + 1. The workspace is provisioned. + 1. The agent starts up and connects to coderd. + 1. The agent starts its bootstrap procedures and completes its startup scripts. 1. The agent reports `ready` status. - Prebuilt workspaces that fail during provisioning are retried with an exponential backoff to prevent transient failures. + Only once the agent reports `ready` is the prebuilt workspace considered eligible. -
+ Prebuilt workspaces that fail during provisioning are retried with a backoff to prevent transient failures. -1.
When a developer selects requests a new workspace, the claiming process occurs: +1. When a developer requests a new workspace, the claiming process occurs: 1. Developer selects a template and preset that has prebuilt workspaces configured. - 1. If an eligible prebuilt workspace exists, it's automatically assigned to the user. - 1. Ownership transfers from the `prebuilds` user to the requesting user. + 1. If an eligible prebuilt workspace exists, ownership transfers from the `prebuilds` user to the requesting user. 1. The workspace name changes to the user's requested name. - 1. The process is transparent to the developer - they simply see a workspace ready faster than normal. + 1. `terraform apply` is executed using the new ownership details, which may affect the [`coder_workspace`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace) and + [`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner) + datasources (see [Preventing resource replacement](#preventing-resource-replacement) for further considerations). - This ownership transfer happens automatically without any special action required by the user. - -
+ The process is transparent to the developer - they simply see a workspace ready faster than normal. You can view available prebuilt workspaces in the **Workspaces** view in the Coder dashboard: ![A prebuilt workspace in the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) +_Note the search term `owner:prebuilds`._ + ### Template updates and the prebuilt workspace lifecycle -Prebuilt workspaces have specific behavior when templates are updated: +Prebuilt workspaces are not updated after they are provisioned. + +When a template's active version is updated: -1. When a template version changes, prebuilt workspaces for old versions are automatically deleted. +1. Prebuilt workspaces for old versions are automatically deleted. 1. New prebuilt workspaces are created for the active template version. -1. Prebuilt workspaces aren't automatically updated after creation. -1. If dependencies change (e.g., an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) update) without a template version change, you can: - - Delete the existing prebuilt workspaces manually. +1. If dependencies change (e.g., an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) update) without a template version change: + - You may delete the existing prebuilt workspaces manually. - Coder will automatically create new prebuilt workspaces with the updated dependencies. The system always maintains the desired number of prebuilt workspaces for the active template version. -### Relationship to workspace presets - -Prebuilt workspaces are tightly integrated with [workspace presets](./parameters.md#workspace-presets-beta): - -1. Each prebuilt workspace is associated with a specific template preset. -1. The preset must define all required parameters needed to build the workspace. -1. The preset parameters define the base configuration and are immutable after they're claimed. -1. Parameters that are not defined in the preset can still be customized by users when they claim a workspace. - ## Administration and troubleshooting ### Managing resource quotas @@ -129,7 +124,7 @@ If a quota is exceeded, the prebuilt workspace will fail provisioning the same w When a prebuilt workspace is claimed, another `terraform apply` run occurs with new values for the workspace owner and name. -This can cause issues: +This can cause issues in the following scenario: 1. The workspace is initially created with values from the `prebuilds` user and a random name. 1. After claiming, various workspace properties change (ownership, name, and potentially other values), which Terraform sees as configuration drift. From 596d7a0f489ec79c1332cc5a4194ebc03fca07c5 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Wed, 7 May 2025 14:27:13 +0000 Subject: [PATCH 15/17] md/copy --- .../templates/extending-templates/prebuilt-workspaces.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 2ea737e3f69d0..ae7150e2df4b0 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -30,7 +30,7 @@ Prebuilt workspaces are tightly integrated with [workspace presets](./parameters ## Enable prebuilt workspaces for template presets -In your template, add a `prebuilds` block within a `coder_workspace_preset` definition to identify how many prebuilt +In your template, add a `prebuilds` block within a `coder_workspace_preset` definition to identify the number of prebuilt instances your Coder deployment should maintain: ```hcl @@ -71,7 +71,7 @@ Prebuilt workspaces follow a specific lifecycle from creation through eligibilit 1. The agent starts its bootstrap procedures and completes its startup scripts. 1. The agent reports `ready` status. - Only once the agent reports `ready` is the prebuilt workspace considered eligible. + After the agent reports `ready`, the prebuilt workspace considered eligible to be claimed. Prebuilt workspaces that fail during provisioning are retried with a backoff to prevent transient failures. @@ -84,12 +84,11 @@ Prebuilt workspaces follow a specific lifecycle from creation through eligibilit [`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner) datasources (see [Preventing resource replacement](#preventing-resource-replacement) for further considerations). - The process is transparent to the developer - they simply see a workspace ready faster than normal. + The process is invisible to the developer - their workspace is ready faster than usual. You can view available prebuilt workspaces in the **Workspaces** view in the Coder dashboard: ![A prebuilt workspace in the dashboard](../../../images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png) - _Note the search term `owner:prebuilds`._ ### Template updates and the prebuilt workspace lifecycle From 14998a050339001183c5ecfe843c293fc6870982 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Wed, 7 May 2025 14:44:17 +0000 Subject: [PATCH 16/17] invisible/transparent --- docs/admin/templates/extending-templates/prebuilt-workspaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index ae7150e2df4b0..aefcbe75e8290 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -84,7 +84,7 @@ Prebuilt workspaces follow a specific lifecycle from creation through eligibilit [`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner) datasources (see [Preventing resource replacement](#preventing-resource-replacement) for further considerations). - The process is invisible to the developer - their workspace is ready faster than usual. + The developer doesn't see the claiming process—the workspace is ready faster than usual. You can view available prebuilt workspaces in the **Workspaces** view in the Coder dashboard: From ac21df134674445a879bde52364dd6c092f4ee62 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Fri, 9 May 2025 09:20:09 +0200 Subject: [PATCH 17/17] chore: update provider version Signed-off-by: Danny Kopping --- .../templates/extending-templates/prebuilt-workspaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index aefcbe75e8290..bbff3b7f15747 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -25,7 +25,7 @@ Prebuilt workspaces are tightly integrated with [workspace presets](./parameters ## Prerequisites - [**Premium license**](../../licensing/index.md) -- **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.3.0-pre2`. (**TODO: update with latest version**) +- **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.4.0`. - **Feature flag**: Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments). ## Enable prebuilt workspaces for template presets @@ -84,7 +84,7 @@ Prebuilt workspaces follow a specific lifecycle from creation through eligibilit [`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner) datasources (see [Preventing resource replacement](#preventing-resource-replacement) for further considerations). - The developer doesn't see the claiming process—the workspace is ready faster than usual. + The developer doesn't see the claiming process — the workspace will just be ready faster than usual. You can view available prebuilt workspaces in the **Workspaces** view in the Coder dashboard: