Standalone Activities - Go SDK
Temporal Go SDK support for Standalone Activities is at Pre-release.
All APIs are experimental and may be subject to backwards-incompatible changes.
Standalone Activities are Activity Executions that run independently, without being orchestrated by a Workflow. Instead
of starting an Activity from within a Workflow Definition using workflow.ExecuteActivity(), you start a Standalone
Activity directly from a Temporal Client using client.ExecuteActivity().
The Activity definition and Worker registration are identical to regular Activities, and only the execution path differs.
This page covers the following:
- Get Started with Standalone Activities
- Define your Activity
- Run a Worker with the Activity registered
- Execute a Standalone Activity
- Get the result of a Standalone Activity
- Get a handle to an existing Standalone Activity
- List Standalone Activities
- Count Standalone Activities
- Run Standalone Activities with Temporal Cloud
This documentation uses source code from the standalone-activity/helloworld.
Get Started with Standalone Activities
Prerequisites:
-
Go 1.22+
-
Temporal Go SDK (v1.41.0 or higher)
-
Temporal CLI (Pre-release version with Standalone Activity support)
Download for your platform:
macOS (Apple Silicon)curl -L https://github.com/temporalio/cli/releases/download/v1.6.2-standalone-activity/temporal_cli_1.6.2-standalone-activity_darwin_arm64.tar.gz | tar xzmacOS (Intel)curl -L https://github.com/temporalio/cli/releases/download/v1.6.2-standalone-activity/temporal_cli_1.6.2-standalone-activity_darwin_amd64.tar.gz | tar xzLinux (arm64)curl -L https://github.com/temporalio/cli/releases/download/v1.6.2-standalone-activity/temporal_cli_1.6.2-standalone-activity_linux_arm64.tar.gz | tar xzLinux (amd64)curl -L https://github.com/temporalio/cli/releases/download/v1.6.2-standalone-activity/temporal_cli_1.6.2-standalone-activity_linux_amd64.tar.gz | tar xzVerify the installation:
./temporal --version
# temporal version 1.6.2-standalone-activity (Server 1.31.0-151.2, UI 2.47.2)Move the binary to your PATH or run it from the current directory as
./temporal.dangerIf you see
Standalone activity is disabledwhen running commands, you are using the standard Temporal CLI instead of the pre-release version above. The standardbrew install temporalorbrew upgrade temporaldoes not include Standalone Activity support during Pre-release.
Start the Temporal development server:
./temporal server start-dev
This command automatically starts the Temporal development server with the Web UI, and creates the default Namespace.
It uses an in-memory database, so do not use it for real use cases.
All code samples on this page use
envconfig.MustLoadDefaultClientOptions()
to configure the Temporal Client connection. It responds to environment
variables and TOML configuration
files, so the same code works against a local dev
server and Temporal Cloud without changes. See Run Standalone Activities with Temporal
Cloud below.
The Temporal Server should now be available for client connections on localhost:7233, and the
Temporal Web UI should now be accessible at http://localhost:8233. Standalone
Activities are available from the nav bar item located towards the top left of the page:
Clone the samples-go repository to follow along:
git clone https://github.com/temporalio/samples-go.git
cd samples-go
The sample project is structured as follows:
standalone-activity/helloworld/
├── activity.go
├── worker/
│ └── main.go
└── starter/
└── main.go
Define your Activity
Define your Activity in a shared file so that both the Worker and starter can reference it.
standalone-activity/helloworld/activity.go
package helloworld
import (
"context"
"go.temporal.io/sdk/activity"
)
func Activity(ctx context.Context, name string) (string, error) {
logger := activity.GetLogger(ctx)
logger.Info("Activity", "name", name)
return "Hello " + name + "!", nil
}
Run a Worker with the Activity registered
Running a Worker for Standalone Activities is the same as running a Worker for Workflow-driven Activities — you create a
Worker, register the Activity, and call Run(). The Worker doesn't need to know whether the Activity will be invoked
from a Workflow or as a Standalone Activity.
See How to develop a Worker in Go for more details on Worker setup and configuration options.
standalone-activity/helloworld/worker/main.go
package main
import (
"github.com/temporalio/samples-go/standalone-activity/helloworld"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/contrib/envconfig"
"go.temporal.io/sdk/worker"
"log"
)
func main() {
c, err := client.Dial(envconfig.MustLoadDefaultClientOptions())
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer c.Close()
w := worker.New(c, "standalone-activity-helloworld", worker.Options{})
w.RegisterActivity(helloworld.Activity)
err = w.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Unable to start worker", err)
}
}
Open a new terminal, navigate to the samples-go directory, and run the Worker:
go run standalone-activity/helloworld/worker/main.go
Leave this terminal running - the Worker needs to stay up to process activities.
Execute a Standalone Activity
Use client.ExecuteActivity() to start a Standalone Activity
Execution. This is called from application code (for example, a starter program), not from inside a Workflow Definition.
ExecuteActivity returns an ActivityHandle that you
can use to get the result, describe, cancel, or terminate the Activity.
The following starter program demonstrates how to execute a Standalone Activity, get its result, list activities, and count activities:
standalone-activity/helloworld/starter/main.go
package main
import (
"context"
"github.com/temporalio/samples-go/standalone-activity/helloworld"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/contrib/envconfig"
"log"
"time"
)
func main() {
c, err := client.Dial(envconfig.MustLoadDefaultClientOptions())
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer c.Close()
activityOptions := client.StartActivityOptions{
ID: "standalone_activity_helloworld_ActivityID",
TaskQueue: "standalone-activity-helloworld",
ScheduleToCloseTimeout: 10 * time.Second,
}
handle, err := c.ExecuteActivity(context.Background(), activityOptions, helloworld.Activity, "Temporal")
if err != nil {
log.Fatalln("Unable to execute activity", err)
}
log.Println("Started standalone activity", "ActivityID", handle.GetID(), "RunID", handle.GetRunID())
var result string
err = handle.Get(context.Background(), &result)
if err != nil {
log.Fatalln("Unable get standalone activity result", err)
}
log.Println("Activity result:", result)
resp, err := c.ListActivities(context.Background(), client.ListActivitiesOptions{
Query: "TaskQueue = 'standalone-activity-helloworld'",
})
if err != nil {
log.Fatalln("Unable to list activities", err)
}
log.Println("ListActivity results")
for info, err := range resp.Results {
if err != nil {
log.Fatalln("Error iterating activities", err)
}
log.Printf("\tActivityID: %s, Type: %s, Status: %v\n",
info.ActivityID, info.ActivityType, info.Status)
}
resp1, err := c.CountActivities(context.Background(), client.CountActivitiesOptions{
Query: "TaskQueue = 'standalone-activity-helloworld'",
})
if err != nil {
log.Fatalln("Unable to count activities", err)
}
log.Println("Total activities:", resp1.Count)
}
You can pass the Activity as either a function reference or a string Activity type name:
handle, err := c.ExecuteActivity(ctx, options, helloworld.Activity, "arg1")
// Using a string type name
handle, err := c.ExecuteActivity(ctx, options, "Activity", "arg1")
client.StartActivityOptions requires ID, TaskQueue, and at least one of ScheduleToCloseTimeout or
StartToCloseTimeout. See StartActivityOptions
in the API reference for the full set of options.
To run the starter:
- Make sure the Temporal Server is running (from the Get Started step above).
- Make sure the Worker is running (from the Run a Worker step above).
- Open a new terminal, navigate to the
samples-godirectory, and run:
go run standalone-activity/helloworld/starter/main.go
Or use the Temporal CLI to execute a Standalone Activity:
./temporal activity execute \
--type Activity \
--activity-id standalone_activity_helloworld_ActivityID \
--task-queue standalone-activity-helloworld \
--schedule-to-close-timeout 10s \
--input '"Temporal"'
Get the result of a Standalone Activity
Use ActivityHandle.Get() to block until the Activity completes and retrieve its result. This is analogous to calling
Get() on a WorkflowRun.
var result string
err = handle.Get(context.Background(), &result)
if err != nil {
log.Fatalln("Activity failed", err)
}
log.Println("Activity result:", result)
If the Activity completed successfully, the result is deserialized into the provided pointer. If the Activity failed, the failure is returned as an error.
Or use the Temporal CLI to wait for a result by Activity ID:
./temporal activity result --activity-id my-standalone-activity-id
Get a handle to an existing Standalone Activity
Use client.GetActivityHandle() to create a handle to a previously started Standalone Activity. This is analogous to
client.GetWorkflow() for Workflow Executions.
Both ActivityID and RunID are required.
handle := c.GetActivityHandle(client.GetActivityHandleOptions{
ActivityID: "my-standalone-activity-id",
RunID: "the-run-id",
})
// Use the handle to get the result, describe, cancel, or terminate
var result string
err := handle.Get(context.Background(), &result)
if err != nil {
log.Fatalln("Unable to get activity result", err)
}
List Standalone Activities
Use client.ListActivities() to list Standalone Activity
Executions that match a List Filter query. The result contains an iterator that yields
ActivityExecutionInfo entries.
These APIs return only Standalone Activity Executions. Activities running inside Workflows are not included.
resp, err := c.ListActivities(context.Background(), client.ListActivitiesOptions{
Query: "TaskQueue = 'standalone-activity-helloworld'",
})
if err != nil {
log.Fatalln("Unable to list activities", err)
}
for info, err := range resp.Results {
if err != nil {
log.Fatalln("Error iterating activities", err)
}
log.Printf("ActivityID: %s, Type: %s, Status: %v\n",
info.ActivityID, info.ActivityType, info.Status)
}
Or use the Temporal CLI:
./temporal activity list
The Query field accepts the same List Filter syntax used for Workflow Visibility. For example,
"ActivityType = 'Activity' AND Status = 'Running'".
Count Standalone Activities
Use client.CountActivities() to count Standalone Activity
Executions that match a List Filter query. This returns the total count of executions (running,
completed, failed, etc.) - not the number of queued tasks. It works the same way as counting Workflow Executions.
resp, err := c.CountActivities(context.Background(), client.CountActivitiesOptions{
Query: "TaskQueue = 'standalone-activity-helloworld'",
})
if err != nil {
log.Fatalln("Unable to count activities", err)
}
log.Println("Total activities:", resp.Count)
Or use the Temporal CLI:
./temporal activity count
Run Standalone Activities with Temporal Cloud
The code samples on this page use envconfig.MustLoadDefaultClientOptions(), so the same code
works against Temporal Cloud - just configure the connection via environment variables or a TOML
profile. No code changes are needed.
For a step-by-step guide on connecting to Temporal Cloud, including Namespace creation, certificate generation, and authentication setup in the Cloud UI, see Connect to Temporal Cloud.
Connect with mTLS
Set these environment variables with values from your Temporal Cloud Namespace settings:
export TEMPORAL_ADDRESS=<your-namespace>.<your-account-id>.tmprl.cloud:7233
export TEMPORAL_NAMESPACE=<your-namespace>.<your-account-id>
export TEMPORAL_TLS_CLIENT_CERT_PATH='path/to/your/client.pem'
export TEMPORAL_TLS_CLIENT_KEY_PATH='path/to/your/client.key'
Connect with an API key
Set these environment variables with values from your Temporal Cloud API key settings:
export TEMPORAL_ADDRESS=<region>.<cloud_provider>.api.temporal.io:7233
export TEMPORAL_NAMESPACE=<your-namespace>.<your-account-id>
export TEMPORAL_API_KEY=<your-api-key>
Then run the Worker and starter code as shown in the earlier sections.