This guide takes you from zero to a running (and self-deleting) VM.
1. Add the dependencies
def deps do
[
{:gcp_compute, "~> 0.1.0"},
{:goth, "~> 1.4"} # recommended token provider (optional dependency)
]
end2. Authenticate
GcpCompute needs an OAuth2 access token for every request. In production that
comes from Goth, which picks up Application Default
Credentials (a service-account key via GOOGLE_APPLICATION_CREDENTIALS, the GCE
metadata server, or gcloud auth application-default login in dev).
Start a Goth server in your supervision tree:
# lib/my_app/application.ex
children = [
{Goth, name: MyApp.Goth}
# ...
]Auth is pluggable — see GcpCompute.TokenProvider to use workload identity, the
metadata server, Vault, etc. instead of Goth.
3. Build a config
A GcpCompute.Config is a cheap, immutable handle you build once and pass to
every call. There is no process to supervise — Req handles connection
pooling internally.
{:ok, config} =
GcpCompute.Config.production(
project: "my-project",
zone: "us-central1-a",
goth: MyApp.Goth
)Or load it from application env with GcpCompute.Config.from_env/2.
4. Spawn a VM and wait for it
insert_instance_and_wait/3 inserts the instance, polls the resulting operation
to completion, and returns the live GcpCompute.Instance:
{:ok, instance} =
GcpCompute.insert_instance_and_wait(config,
name: "worker-1",
machine_type: "e2-micro",
spot: true, # cheap, preemptible, deletes on termination
max_run_duration: 3600, # hard server-side TTL (seconds)
startup_script: "#!/bin/bash\necho ready > /tmp/ready",
labels: %{"owner" => "platform"}
)
GcpCompute.Instance.external_ip(instance) #=> "34.x.x.x"See Configuring machines for every spec/1 option.
5. Drive the lifecycle yourself (optional)
Every mutating call returns an GcpCompute.Operation you can poll explicitly:
{:ok, op} = GcpCompute.insert_instance(config, name: "worker-1", machine_type: "e2-micro")
{:ok, _done} = GcpCompute.wait_for_operation(config, op, timeout: :timer.minutes(3))
{:ok, vm} = GcpCompute.get_instance(config, "worker-1")6. Tear it down
{:ok, _op} = GcpCompute.delete_instance_and_wait(config, "worker-1")(Spot VMs with max_run_duration also delete themselves server-side — belt and
suspenders.)
Error handling
Every function returns {:ok, _} or {:error, %GcpCompute.Error{}}. The error
carries a coarse :reason you can match on:
case GcpCompute.insert_instance(config, name: "w", machine_type: "e2-micro") do
{:ok, op} -> op
{:error, %GcpCompute.Error{reason: :api_error, status: 403, message: msg}} -> {:denied, msg}
{:error, %GcpCompute.Error{reason: :operation_failed} = err} -> {:failed, err}
{:error, %GcpCompute.Error{reason: :timeout}} -> :timed_out
{:error, %GcpCompute.Error{reason: :transport}} -> :network
endObservability
Attach a quick logger during development, or wire the spans into
Telemetry.Metrics:
GcpCompute.Telemetry.attach_default_logger()See GcpCompute.Telemetry for the full event list.