Writing a Terraform-style workflow for Spark deployments
GPUwerk doesn't currently publish a Terraform provider, so there's no gpuwerk_spark resource block to write. What you get instead is a console and a REST API, and if your team runs infrastructure as code for everything else, the honest option is treating a Spark deployment as scripted API calls that live in the same repo and get reviewed the same way, rather than pretending a provider exists that doesn't.
What "Terraform-style" means without a provider
The part of Terraform worth keeping even without a native provider is the discipline: infrastructure described as a file, reviewed as a diff, applied through a pipeline instead of a person clicking through a console. A shell script or small program that calls GPUwerk's API, checked into the same repo as the rest of your deployment config and run from CI, gets you that discipline without a provider binary to build and maintain. It won't give you Terraform's plan-diff view of exactly what's changing, which is the real thing you give up.
Scripting the API directly, the shape of it
A provisioning script that's idempotent, safe to run again if it fails partway, is the part worth getting right regardless of whether Terraform is involved at all:
# provision-spark.sh
#!/bin/bash
set -euo pipefail
NODE_NAME="inference-prod-01"
EXISTING=$(curl -s -H "Authorization: Bearer $GPUWERK_API_KEY" \
"https://console.gpuwerk.com/api/v1/nodes?name=$NODE_NAME")
if [ "$(echo "$EXISTING" | jq '.nodes | length')" = "0" ]; then
curl -s -X POST "https://console.gpuwerk.com/api/v1/nodes" \
-H "Authorization: Bearer $GPUWERK_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"name\": \"$NODE_NAME\", \"region\": \"eu-central\", \"type\": \"dgx-spark\"}"
else
echo "$NODE_NAME already exists, skipping create"
fi
Store the desired state, node names, regions, counts, as a small config file the script reads rather than hardcoding values inline, and that file becomes the thing you diff in a pull request, the closest equivalent to a Terraform plan without Terraform doing the diffing for you.
Wrapping the API with Terraform's generic providers, and where it falls short
If the team is already committed to Terraform and would rather keep everything in one tool, the http provider can do read-only lookups against the API, and null_resource with a local-exec provisioner can shell out to a create or destroy script:
resource "null_resource" "spark_node" {
triggers = {
node_name = "inference-prod-01"
}
provisioner "local-exec" {
command = "./provision-spark.sh create ${self.triggers.node_name}"
}
provisioner "local-exec" {
when = destroy
command = "./provision-spark.sh destroy ${self.triggers.node_name}"
}
}
Be clear-eyed about what this buys you: Terraform tracks that the resource exists in its state file, but it isn't validating the actual node state against the API the way a native provider's Read function would, so drift between what Terraform thinks exists and what's actually running on GPUwerk is possible in a way it wouldn't be with a purpose-built provider. For most teams provisioning a handful of Sparks, the plain script from the previous section is less to maintain than getting this pattern right.
Tie provisioning into the same pipeline as everything else
Whichever approach you pick, running it from the same CI pipeline that handles model updates keeps infrastructure changes and application changes reviewed the same way. If you're already using the model-swap pipeline from setting up a CI pipeline for model updates, a provisioning step ahead of it, spin up the node before the deploy job needs it, fits naturally into the same workflow rather than living as a separate manual step someone has to remember.
FAQ
Does GPUwerk have an official Terraform provider?
No, not currently. What GPUwerk offers is a console and a REST API for provisioning and managing Spark nodes. A Terraform-style workflow against GPUwerk today means either scripting the API directly from your deployment pipeline, or wrapping it yourself in Terraform's generic http or external providers, not pointing Terraform at a purpose-built gpuwerk provider from the registry.
Is it worth writing a local Terraform provider just for one API?
For a single Spark or a small handful, usually not. Terraform's provider SDK is real Go code with its own build and release process, more infrastructure than the problem needs if you're provisioning a couple of nodes a month. A shell script or a small Python tool calling the API directly, run from the same CI pipeline, gets you the version-controlled, repeatable part without maintaining a provider binary. A custom provider starts paying off once enough of your team is already fluent in Terraform and the provisioning volume justifies the upfront build.
Can Terraform's generic providers call a REST API without a purpose-built provider?
Yes, within limits. The http provider can make read-only calls, useful for looking up existing state, but it isn't built for the full create-update-delete lifecycle a resource block expects. The external provider can shell out to a script for read operations. Neither replicates a proper provider's plan-and-apply semantics for a stateful resource like a running Spark node, so most teams scripting against an API without a native provider end up with a plain script wrapped by Terraform's null_resource and local-exec provisioners, or skip Terraform for that resource and drive the API directly from CI.