Temporal Cloud runs the Temporal server for you: the four services, persistence, visibility, upgrades, and authentication. It does not run your workers. A worker is the process that executes your workflow and activity code, and Temporal's position is that you bring the compute. This post is about that remaining part: what a worker needs, the places you can run one, and how we made it a single service block in Specific.
What a worker is
A worker connects to a Temporal server, polls one or more task queues, and runs your code: workflow code in a deterministic sandbox, activity code as normal functions. The server hands out tasks, the worker executes them and reports back. You can run several workers against one task queue and the server spreads the work; stop one and its in-flight tasks are retried elsewhere once their timeouts pass. All durable state lives in the server, so the worker is stateless from the host's point of view.
That makes a worker an ordinary long-running process with three inputs: the server address, the namespace, and a credential. Against the dev server the credential is empty; against Temporal Cloud it is an API key or an mTLS certificate. In TypeScript:
import { NativeConnection, Worker } from "@temporalio/worker";
import * as activities from "./activities";
const apiKey = process.env.TEMPORAL_API_KEY;
const connection = await NativeConnection.connect({
address: process.env.TEMPORAL_ADDRESS!,
tls: apiKey ? true : null,
...(apiKey ? { apiKey } : {}),
});
const worker = await Worker.create({
connection,
namespace: process.env.TEMPORAL_NAMESPACE!,
taskQueue: "jobs",
workflowsPath: require.resolve("./workflows"),
activities,
});
await worker.run();Reading the three values from the environment is what lets one binary run against the dev server locally and Temporal Cloud in production. The other SDKs (Python, Go, Java, .NET, PHP) have the same shape.
What the host has to provide
Whatever runs the worker has to cover the same list:
- A build. The worker is application code with dependencies, so it needs an image or an artifact, rebuilt on every change.
- Credentials per environment. A namespace and API key for production, another for staging, the dev server locally. They have to reach the process as environment variables, and only that process.
- Restarts. The process must come back after a crash or a host restart. Temporal's SDKs retry polling on their own when the server is unreachable, so the host's only job is to keep the process alive.
- Graceful shutdown. Every deploy stops a worker. The SDK handles
SIGTERMby default: it stops polling, lets in-flight activities finish for a grace period, then force-stops. The host has to send that signal to the worker process itself and wait at least as long as the grace period before killing the container. - Scaling. Workers wait on the network most of the time, so CPU is a poor signal. Temporal's guidance is to scale on schedule-to-start latency or task queue backlog, from the SDK's own metrics.
- Logs and metrics. A worker has no HTTP endpoint, so nothing scrapes it unless you export the SDK metrics somewhere.
Where you can run one
Any host that runs a long-lived process works. What differs is how much of the list above you do by hand.
- A VM with systemd. Simplest to start. You write the unit file, copy credentials into an environment file, and rebuild and restart on every deploy. Restarts and signal handling are covered; builds, per-environment credentials, and metrics are not.
- A container platform. Fly.io, Railway, Render, and similar run a container from your repository and restart it. Credentials go into their secrets store per app or environment, and you configure the stop signal and grace period in their settings. Scaling on Temporal metrics means exporting them and wiring an external autoscaler.
- Kubernetes. The most control and the most to write: a Deployment with a rolling strategy, a termination grace period above the SDK's, secrets per namespace, a metrics exporter, and an HPA or KEDA on schedule-to-start latency. Temporal's Kubernetes tips and Worker Controller cover this route.
In all three, the worker sits outside whatever hosts the rest of your app, or gets bolted on with its own configuration. Credentials, in particular, are something you create in the Temporal Cloud console and copy over, once per environment.
On Specific
On Specific, a worker is a service block. It has a build and a command, and no endpoint, because nothing calls it:
temporal "jobs" {}
postgres "main" {}
build "worker" {
base = "node"
command = "npm run build"
}
service "worker" {
build = build.worker
command = "node dist/worker.js"
env = {
TEMPORAL_ADDRESS = temporal.jobs.url
TEMPORAL_NAMESPACE = temporal.jobs.namespace
TEMPORAL_API_KEY = temporal.jobs.api_key
DATABASE_URL = postgres.main.url
}
dev {
command = "npm run worker:dev"
}
}The temporal "jobs" {} block is the engine. Going down the list from before:
- Build: the
buildblock, built in the same pipeline as your API and frontend. - Credentials: the three
temporal.jobs.*references. Underspecific devthey resolve to a local dev server with persistent storage. Underspecific deploy, the platform provisions a Temporal Cloud namespace for the environment, creates a service account and API key, and injects them. You never open the Temporal Cloud console, and every environment, previews included, gets its own namespace. - Restarts: a worker whose process exits is restarted.
- Graceful shutdown: deploys are rolling, so a new worker instance is available before an old one is stopped, and the old one gets
SIGTERMand about a minute to finish in-flight activities before it is force-stopped. Keep the SDK's grace time under that, or split long work into shorter activities with heartbeats. - Scaling: replica counts are set in the dashboard. Today, Specific's autoscaling is CPU-based, which is the wrong signal for a worker, so set a fixed count and size it from schedule-to-start latency.
- Logs and metrics: the worker's logs and runtime metrics land in the same place as every other service, queryable with
specific query. Export the SDK's own metrics over OTLP if you want slot and latency numbers alongside them.
The database reference is there because a worker usually needs one. It is the same file, the same references, and the same deploy as the rest of the app.
This is also how we run Specific itself. The platform's deploy pipeline is a set of Temporal workflows, and its worker is a rolling deployment with a termination budget long enough for an image build to finish.
Two things to get right in the worker
Run it as the main process. node dist/worker.js, not a wrapper that swallows signals. If a worker seems to crash on server restarts, it is almost always a wrapper reacting to the SDK's logged errors, not the SDK.
Match the grace periods. The SDK's shutdownGraceTime is fiction if the host kills the container sooner. Set the container's period above it, or shorten activities and heartbeat from them so cancellation reaches them.
Common questions
Does a worker need a public URL?
No. It makes outbound connections to Temporal and accepts nothing inbound. Keep it private.
How many workers should I run?
One is fine for a small app: Temporal retries anything the worker was doing when it restarts, so the cost of a single instance is a pause, not lost work. Run two once the app is in real production use and a pause during a deploy or a crash matters. Beyond that, scale on schedule-to-start latency; one worker with more concurrency slots often beats several small ones until you hit CPU or memory limits.
Can I run workflows for different task queues in one deployment?
Yes. A single process can run several workers. Separate deployments are worth it when queues have different scaling needs or you want to deploy them independently.
Do I need Temporal's Worker Versioning?
Only if workflows live long enough that a deploy can land mid-execution and the code change is not backwards compatible. patched() covers most cases; Worker Versioning and the Worker Controller route old executions to old workers instead. Temporal's worker deployment docs cover both.
Try it
A complete worker with workflows, activities, and a task queue UI is in the durable workflows example: live demo, source.
To add a worker to your own project, give your coding agent this prompt:
Help me get started with Specific by following: https://docs.specific.dev/for-ai/onboardingOr install the CLI yourself:
curl -fsSL https://specific.dev/install.sh | shFor more, see the Managed Temporal guide, how to run Temporal locally, and managed Temporal hosting.