Best Temporal Hosting Platforms in 2026
Most Temporal hosting comparisons answer the easy question. Where do I run the cluster? Temporal Cloud, or Kubernetes if you have a reason. Done in a sentence. The question that actually shapes your architecture is the one people ask second, usually after the first production incident: where do the workers run? Workers are your code — long-lived processes that poll task queues, execute activities, and cache workflow state in memory. They are not web servers, they do not respond to requests, and almost every convenience a modern PaaS offers was designed for something else.
So this roundup splits the problem in two and spends most of its length on the half nobody shops for. I founded PandaStack, which appears in the field below and is not the top pick here — Temporal Cloud is the obvious answer for the server half and I will say so plainly. I keep this honest by citing specific numbers only for our own system, describing every third-party platform qualitatively, and telling you to verify anything load-bearing against the vendor's current docs, because limits and pricing move faster than blog posts.
Temporal is two products wearing one trench coat
You are making two hosting decisions, not one, and they have almost nothing in common. Conflating them is why teams end up either self-hosting a Cassandra cluster they did not want or running their workers on a platform that quietly turns them off.
- The server (cluster) — frontend, history, matching, and worker services, plus a real database underneath: Cassandra, PostgreSQL, or MySQL, and typically Elasticsearch if you want advanced visibility search. This is a stateful distributed system with sharding, retention, and archival concerns. It is operationally serious, it is the same for everybody, and it gives you no competitive advantage whatsoever. Most teams should not self-host it.
- The workers — your workflow and activity code, packaged as long-running processes that connect out to the cluster and poll task queues. This half is entirely yours. It carries your dependencies, your secrets, your CPU profile, and your blast radius, and it is where every interesting hosting constraint lives.
- The direction of the connection matters — workers dial out to the cluster; the cluster never dials in. There is no inbound port, no ingress, no load balancer, and no health-check URL unless you add one. That is convenient for networking and inconvenient for platforms that decide whether your process is alive by curling it.
Why workers break normal PaaS assumptions
Five properties of a Temporal worker collide with defaults you would never think to check on a platform built for HTTP services. Each one has produced a real outage somewhere.
- They are pollers, not responders — a worker's steady state is a long-poll against the task queue. There is no request to autoscale on, no p95 latency to alarm on, and no traffic curve. A platform that measures liveness or utilization in HTTP terms sees an idle process doing nothing, which is precisely wrong.
- Scale-to-zero is a silent failure mode — if the platform stops your worker because no HTTP request arrived, nothing errors. The cluster keeps accepting workflow starts, tasks pile up on the queue, and workflows simply stop advancing until someone notices the schedule slipped. A workflow engine whose workers scale to zero is a very reliable way to schedule work that never happens.
- Shutdown is a data problem, not a deploy detail — a worker killed mid-activity means that activity times out and retries. If it is idempotent, you paid for a retry. If it is not, you charged the card twice. The platform's grace period between SIGTERM and SIGKILL is therefore load-bearing infrastructure, and on many platforms it is short and configurable only if you know to look.
- Sticky execution means restarts cost replay — workers cache workflow state in memory and the cluster routes subsequent tasks back to the same worker. Kill it and the next worker rebuilds that state by replaying history from the beginning. For long histories that is real CPU and real latency, so a platform that recycles instances aggressively taxes you continuously.
- Activities can run far longer than a function invocation — a video transcode, a large export, a two-hour ETL. Temporal is designed for exactly this, with heartbeats and long start-to-close timeouts. Serverless execution ceilings are not.
Graceful shutdown, concretely
The worker SDK already knows how to drain: stop polling for new tasks, let in-flight activities finish, report their results, then exit. Your job is to catch the platform's termination signal and to make sure the platform's kill deadline is longer than your drain window. Get the ordering wrong and every rolling deploy becomes a burst of activity retries.
import asyncio
import signal
from datetime import timedelta
from temporalio.client import Client
from temporalio.worker import Worker
from my_app.activities import charge_card, ship_order
from my_app.workflows import OrderWorkflow
async def main() -> None:
client = await Client.connect(
"my-namespace.acct.tmprl.cloud:7233",
namespace="my-namespace",
)
# graceful_shutdown_timeout is the whole point of this file: on shutdown
# the worker stops polling immediately, then gives in-flight activities
# this long to finish before their cancellation is escalated.
worker = Worker(
client,
task_queue="orders",
workflows=[OrderWorkflow],
activities=[charge_card, ship_order],
graceful_shutdown_timeout=timedelta(seconds=90),
max_concurrent_activities=20,
)
stop = asyncio.Event()
loop = asyncio.get_running_loop()
for sig in (signal.SIGINT, signal.SIGTERM):
# SIGTERM is what your platform sends before it sends SIGKILL.
# Catch it, or every deploy converts in-flight activities into retries.
loop.add_signal_handler(sig, stop.set)
async with worker:
await stop.wait()
# Leaving the context manager drains the worker: stop polling, finish what
# is running, report the results, THEN exit.
if __name__ == "__main__":
asyncio.run(main())
# The platform-side half of the contract: its SIGKILL deadline must be LONGER
# than graceful_shutdown_timeout, and longer than your slowest activity that
# cannot be safely retried. If the platform kills at 30s and you drain for 90s,
# this code is decorative.Determinism, replay, and why hard cutovers hurt
Workflow code must be deterministic, because the cluster reconstructs workflow state by replaying event history through your code. That is the mechanism behind durable execution, and it has a hosting consequence people discover late: a workflow that started last Tuesday may still be running when you deploy today, and its history must replay cleanly against whatever code is now handling it. Change the order of activity calls, add a branch, swap a library that returns a map in a different order, and you get a non-determinism error on a workflow that was perfectly healthy an hour ago.
The mitigations — patching and worker versioning with build IDs — both assume you can run old and new worker builds at the same time, with the cluster routing pinned workflows to the version that started them. So a platform that only knows how to do a hard cutover deploy actively fights this. What you want is the ability to run several worker deployments concurrently, on separate task queues or separate build IDs, and to retire an old build when its workflows have drained rather than when the pipeline is done. Anything that replaces every instance in one atomic step is a bad fit, and 'we'll just wait for workflows to finish' stops being viable the first time you write one that runs for a month.
The field: where to run each half
Each option gets what it is, who it suits, and where it hurts. No invented prices, latencies, or feature checkmarks for anyone but us — verify current limits, grace periods, and pricing against each vendor's own documentation, because all of these ship changes faster than roundups get updated.
1. Temporal Cloud (for the server half)
This is the default answer for the cluster, and I do not think it is close for most teams. Running the server yourself means operating a sharded stateful system on Cassandra or Postgres, plus Elasticsearch for visibility, plus retention and archival policy, plus upgrades that must not lose history. Temporal Cloud takes that entire category away and leaves you with a namespace endpoint, certificates or API keys, and your own workers. Note what it does not do: it does not run your workers. Your code still needs a home, which is the rest of this article.
- Who it's for — essentially everyone who is not contractually prevented from using it. Especially teams whose infra headcount is small enough that the cluster would become one person's whole job.
- Where it hurts — it is a hard dependency on a hosted control plane, with data residency and network egress implications you should check against your compliance requirements. Verify current regions, connectivity options, and pricing model against Temporal's docs; do not take a blog's word for any of it.
2. Self-hosting the cluster on Kubernetes
The legitimate reasons to self-host are data residency, an air-gapped or heavily regulated environment, an existing platform team that already runs stateful systems well, or a workload profile where you have actually modelled the cost and it favors you. The illegitimate reason is that the Helm chart came up on your laptop. A demo cluster is easy; a production one means owning the persistence layer, shard counts, retention, visibility indexing, and version upgrades on a system that is the source of truth for every in-flight workflow in the company.
On the worker side, Kubernetes is genuinely excellent, and this is where the operators come in. A Deployment of workers is close to the ideal shape: no ingress needed, terminationGracePeriodSeconds is exactly the knob the drain code above requires, and running two Deployments on different build IDs during a version migration is trivial. Community and vendor Kubernetes operators exist for managing Temporal clusters and, increasingly, worker deployments as custom resources — worth evaluating if you are already all-in on Kubernetes, and worth ignoring if you are not, since an operator is another thing to upgrade.
- Who it's for — organizations with a real platform team, compliance-driven placement requirements, or an existing Kubernetes estate the workers should sit inside.
- Where it hurts — the cluster is a serious operational commitment and idle worker replicas bill continuously, because Pods do not scale to zero without extra machinery and a poller has no HTTP metric to scale on. Set terminationGracePeriodSeconds deliberately; the default is short enough to truncate a drain.
3. AWS: EKS, ECS, and Fargate
The enterprise default for workers, and a fine one. A long-running ECS service or an EKS Deployment has no request semantics to violate, IAM gives activities scoped credentials without a secrets sprawl, and the whole thing lives inside the account your security team already reviewed. ECS exposes a stop timeout, EKS exposes the Pod grace period, and both are configurable — which is more than several friendlier platforms can say.
- Who it's for — teams already deep in AWS whose workers need to sit beside existing services, VPC-private data stores, and an established IAM story.
- Where it hurts — ceremony and idle cost. Getting from a working worker to a deployed one involves real infrastructure-as-code, autoscaling has no natural HTTP signal to work from (task-queue depth is the metric you actually want, and you will be plumbing it yourself), and warm tasks bill while queues are empty. Confirm current stop-timeout defaults and maximums against AWS docs before you rely on a long drain.
4. Google Cloud: Cloud Run and GKE
GKE behaves like the Kubernetes story above. Cloud Run is the interesting one, because it is the platform people most often try to use for workers and most often get wrong. Cloud Run's request-driven model can throttle or reclaim CPU from an instance that is not serving a request, which is a rough fit for a process whose entire job is to sit in a long poll. Cloud Run also offers worker-pool and always-allocated-CPU style configurations aimed exactly at this problem — verify what is current and generally available before you design around it, because this specific corner of the product has changed repeatedly.
- Who it's for — GCP-native teams; GKE for full control, Cloud Run in a worker-appropriate configuration when you want managed compute without a cluster.
- Where it hurts — the default Cloud Run posture is built for request-response and will happily starve or stop a poller. Read the CPU allocation and instance-lifetime semantics carefully rather than assuming a container that runs forever will, in fact, run forever.
5. Fly.io
Fly Machines are API-driven VMs you can start, stop, and place regionally, which maps nicely onto workers: a machine per task queue, or a machine per region for activities that must run near data. Because it is a VM rather than a request handler, a long poll is unremarkable, and process-group configuration lets you run a worker without pretending it is a web service.
- Who it's for — teams that want VM-shaped compute with programmatic control, regional placement, and the option to stop machines for queues that genuinely have no work.
- Where it hurts — you assemble the platform from primitives: wake logic, deploy strategy across worker versions, and queue-depth-driven scaling are yours to build. Verify current auto-stop and auto-start behavior against Fly's docs and make sure nothing stops a worker that is merely idle rather than genuinely unneeded.
6 & 7. Railway and Render
Both are git-driven container platforms with first-class support for non-web processes, and for a small team this is the shortest path from a worker repo to a running worker. Push, get a process, attach a managed Postgres if you are self-hosting the cluster, and move on. They exist to run long-lived containers, so the fundamental shape is right — the caveats are all in the details of sleep behavior and shutdown timing.
- Who it's for — small teams and early products where effort-to-outcome matters more than control, running workers against Temporal Cloud rather than a self-hosted cluster.
- Where it hurts — check two things explicitly on your plan: whether background or idle services can be slept, and how long the platform waits between SIGTERM and SIGKILL. Also check whether you can run two versions of a worker concurrently, because a strict blue-green replace of every instance is awkward once you have pinned, long-running workflows.
8. Northflank
Northflank sits between a PaaS and a Kubernetes platform: managed container workloads with explicit support for jobs and non-HTTP services, addons for databases, and enough control over deployment strategy to matter here. If Railway feels too opinionated and Kubernetes feels like too much, this is the shape that usually gets shortlisted, including for teams self-hosting the Temporal cluster on managed Postgres in the same place.
- Who it's for — teams that want Kubernetes-grade deployment control without operating a cluster, especially when both the workers and a self-hosted cluster's database should live on one platform.
- Where it hurts — more concepts to learn than a push-to-deploy PaaS, and as always you should verify the graceful-shutdown window and the ability to run multiple concurrent worker versions against their current docs rather than assuming.
9. PandaStack
Ours, so discount accordingly, and let me start with what it is not: PandaStack is not a managed Temporal control plane. There is no hosted cluster here. You bring Temporal Cloud or your own self-hosted cluster, and we host the other half — the workers, and the environments your activities execute in. If you came looking for a managed Temporal server, take Temporal Cloud and stop reading this section.
What we do provide is a Firecracker microVM with a real Linux userspace, running a long-lived process with no request semantics, no HTTP liveness assumption, and no execution ceiling — plus per-sandbox network namespaces and egress control, a managed Postgres if you are self-hosting the cluster (create runs 30–90s), and scale-to-zero for the queues that are honestly idle rather than merely quiet. The part that is actually differentiated is per-activity isolation: an activity can create its own microVM, run there, and be destroyed, which matters enormously if the activity executes code you did not write. Snapshot-restore creates land around 179ms p50 and 203ms p99 (a first cold boot of a fresh template is about 3s), and copy-on-write forking runs 400–750ms same-host and 1.2–3.5s cross-host, so a warm environment can be forked per activity instead of rebuilt.
- Who it's for — workers whose activities run untrusted or customer-supplied code and therefore need a hard isolation boundary per execution; and teams self-hosting a cluster who want the workers, their sandboxes, and the cluster's Postgres on one substrate.
- Where it hurts — no managed control plane, so the cluster decision is still yours to make elsewhere. If your activities are ordinary trusted business logic that calls three internal APIs, you are buying isolation you do not need and a container PaaS or ECS is the better call. Scale-to-zero must also be applied deliberately: an idle worker is only safe to stop if something wakes it when work arrives, and for a busy production queue the correct configuration is simply always-on.
The comparison, in one list
Everything below about third-party platforms is qualitative and subject to change — verify current limits, shutdown grace periods, sleep behavior, and pricing against their own docs before you commit. This is a shortlist tool, not a spec sheet.
- Temporal Cloud — Best for: the server half, for almost everyone; a namespace endpoint instead of a Cassandra operations practice. Watch out for: it does not run your workers, and data residency plus connectivity options need checking against your compliance rules.
- Self-hosted on Kubernetes — Best for: regulated or air-gapped estates, and worker Deployments where terminationGracePeriodSeconds and multi-version rollouts are first-class. Watch out for: the cluster is a genuine operational commitment, and idle replicas bill because a poller gives autoscalers nothing to scale on.
- AWS (EKS / ECS / Fargate) — Best for: workers that must sit inside an existing VPC and IAM perimeter, with configurable stop timeouts. Watch out for: infrastructure ceremony, warm tasks billing against empty queues, and building queue-depth autoscaling yourself.
- Google Cloud (Cloud Run / GKE) — Best for: GCP-native teams; GKE for control, Cloud Run in a worker-appropriate configuration for managed compute. Watch out for: Cloud Run's request-driven defaults can throttle or reclaim CPU from a long-polling process — read the CPU allocation semantics before designing around it.
- Fly.io — Best for: VM-shaped workers with regional placement and programmatic start/stop for queues that are truly idle. Watch out for: you assemble deploy strategy, wake logic, and scaling yourself, and auto-stop behavior must not fire on a worker that is merely quiet.
- Railway — Best for: fastest path from a worker repo to a running worker for a small team on Temporal Cloud. Watch out for: confirm sleep behavior for non-web services and the SIGTERM-to-SIGKILL window on your plan.
- Render — Best for: the same push-to-deploy simplicity with background worker services as a named concept. Watch out for: same two questions — idle sleep and shutdown grace — plus whether you can run two worker versions concurrently during a migration.
- Northflank — Best for: Kubernetes-grade deployment control without running a cluster, including managed Postgres beside a self-hosted Temporal cluster. Watch out for: more surface area to learn than a push-to-deploy PaaS; verify shutdown and multi-version behavior explicitly.
- PandaStack — Best for: activities that execute untrusted or customer-supplied code, where each execution gets its own microVM and kernel; long-lived worker processes with no request semantics. Watch out for: no managed Temporal control plane at all — bring Temporal Cloud or self-host — and it is overkill if your activities are ordinary trusted code.
The case that actually justifies a sandbox: customer-supplied activities
Here is the honest reason a microVM platform appears in a Temporal roundup at all. If your activities are trusted code you wrote, any competent container platform is fine and you should ignore this section. But there is a specific and increasingly common architecture where it is not fine: a workflow-automation product where the customer writes the step. Zapier-shaped integrations with a code block, an ETL tool with user-defined transforms, an agent platform where each tool is a customer script. Temporal is a superb fit for that product — durable retries, visibility, timeouts — and it says nothing at all about where the customer's code executes. By default it executes inside your worker process, next to your cluster credentials, your database connection string, and every other tenant's in-flight data. A shared-kernel container narrows that blast radius; it does not close it, and 'the activity ran a fork bomb and took out the worker for forty other tenants' is a support ticket you can avoid entirely.
import json
from temporalio import activity
from pandastack import Sandbox
@activity.defn
async def run_customer_step(step_id: str, code: str, payload: dict) -> dict:
"""Execute a customer-authored workflow step in its own microVM.
The step gets a fresh guest kernel, rootfs, and network namespace, and
none of this worker's credentials. It is destroyed when the activity ends.
"""
info = activity.info()
sbx = Sandbox.create(
template="code-interpreter",
ttl_seconds=900, # backstop: the VM cannot outlive the activity
metadata={
"step_id": step_id,
"tenant": payload["tenant"],
"workflow_id": info.workflow_id,
"run_id": info.run_id, # ties a VM back to one execution in the UI
},
)
try:
sbx.filesystem.write("/workspace/step.py", code)
sbx.filesystem.write("/workspace/input.json", json.dumps(payload))
# A hard timeout INSIDE the activity, shorter than the activity's own
# start-to-close timeout, so a runaway loop fails as a step error
# rather than as a mysteriously missing heartbeat.
r = sbx.exec("python3 /workspace/step.py", timeout_seconds=300)
if r.exit_code != 0:
# A customer bug is a normal outcome. Surface stderr, keep it
# bounded, and let Temporal's retry policy decide what happens.
raise RuntimeError(f"step exited {r.exit_code}: {r.stderr[-2000:]}")
return {
"output": json.loads(sbx.filesystem.read("/workspace/output.json")),
"stdout": r.stdout[-4000:],
}
finally:
sbx.kill() # VM, disk, and network namespace go away togetherTwo details generalize beyond us. Put the timeout inside the activity and make it shorter than the activity's start-to-close timeout, so a runaway customer loop surfaces as a legible step failure instead of a heartbeat that stopped arriving. And remember that raising here hands control to Temporal's retry policy — which means the step will run again, possibly on a different worker. If the customer's code has side effects, retries are a correctness question and not a resilience feature, so pass an idempotency key derived from the workflow and activity identity and make the retry policy an explicit decision rather than a default you inherited.
How to choose
Decide the server half in about thirty seconds, then spend your energy on the workers. Work down this list and stop at the first line that describes you.
- You need a Temporal cluster and have no regulatory reason to run it yourself — Temporal Cloud. This is not a close call, and self-hosting a sharded stateful system is not a differentiator for your product.
- You are air-gapped, in a residency-constrained jurisdiction, or your platform team already operates stateful systems well — self-host the cluster on Kubernetes, budget real headcount for it, and be honest about the upgrade path for in-flight histories.
- Your workers are ordinary trusted code and you are already in AWS or GCP — EKS/ECS or GKE, with the stop timeout set deliberately and queue depth wired into autoscaling rather than CPU.
- You are a small team and want workers running this afternoon — Railway, Render, or Northflank against Temporal Cloud; confirm the sleep behavior and the shutdown grace period before you ship, not after.
- You need regional placement, VM-shaped compute, or programmatic control over worker lifecycle — Fly Machines, and accept that you are assembling the deploy and scaling story yourself.
- Your activities execute code your customers wrote — the isolation boundary is now the top requirement, not a footnote. Pick per-execution hardware virtualization: PandaStack for a managed version, or Kubernetes plus gVisor or Kata if you would rather own it.
- You are considering serverless functions for workers — do not. Use them for the thing that starts a workflow or receives a callback, and let a real process hold the poll.
Whatever you shortlist, prove it with a spike instead of a spreadsheet. Deploy a worker running one deliberately slow, deliberately non-idempotent activity, start a workflow, and then deploy over it while that activity is running. If the activity completes exactly once, the platform respects your drain window. If it retries, you just found the constraint that would otherwise have found you during an incident — and you found it on a Tuesday afternoon with nothing at stake.
Frequently asked questions
Should I self-host the Temporal server or use Temporal Cloud?
Use Temporal Cloud unless something specific stops you. Self-hosting means operating a sharded stateful system on Cassandra, PostgreSQL, or MySQL, usually with Elasticsearch for advanced visibility, plus retention policy, archival, shard sizing, and version upgrades against a database that is the source of truth for every in-flight workflow you have. That is a real operations practice, and none of it differentiates your product. The legitimate reasons to self-host are data residency rules, air-gapped environments, an existing platform team that already runs stateful systems competently, or a workload where you have genuinely modelled the economics. Wanting to avoid a vendor is not the same as having modelled it. Verify current regions, connectivity, and pricing against Temporal's own documentation.
Can Temporal workers run on serverless functions?
Not as workers, no. A worker is a long-lived process that holds a long poll against a task queue, caches workflow state in memory for sticky execution, and needs a graceful drain when it shuts down. Serverless functions have execution ceilings, no persistent process to poll from, and a lifecycle you do not control — and activities in Temporal routinely run far longer than a function invocation is allowed to. The deeper problem is silent failure: if the platform stops your worker because no HTTP request arrived, nothing errors. Tasks accumulate on the queue and workflows simply stop advancing until someone notices. Functions are excellent around Temporal — starting workflows, receiving callbacks, sending signals — and a poor fit as the worker runtime itself.
Where should I run activities that execute untrusted or customer-supplied code?
Not in the worker process. By default a customer-authored step runs alongside your cluster credentials, your database connection string, and every other tenant's in-flight activity data, and a single runaway loop or memory hog degrades all of them. Execute that code in a disposable environment created for the activity and destroyed afterward, with its own kernel, its own filesystem, no ambient credentials, and an explicit timeout shorter than the activity's start-to-close timeout. Hardware-virtualized microVMs per execution are the strong version; gVisor or Kata on Kubernetes is a reasonable self-owned alternative. Also settle idempotency deliberately, because when that activity fails Temporal will retry it — possibly on a different worker — and for code with side effects retries are a correctness question.
How do I deploy new worker code without breaking running workflows?
Assume something started last week is still running. Workflow code must be deterministic because the cluster replays event history through it, so a reordered activity call or a new branch can produce a non-determinism error on a workflow that was healthy an hour ago. Temporal's answers are patching and worker versioning with build IDs, and both assume you can run old and new worker builds concurrently while pinned workflows drain to the version that started them. That makes concurrent multi-version deployment a hosting requirement, not a nicety — a platform that only performs hard cutover replacement of every instance fights this directly. Check the current versioning guidance for your SDK, since this area has changed significantly across releases.
How long should the platform's shutdown grace period be?
Longer than your worker's graceful shutdown timeout, which in turn should be longer than the slowest activity you cannot safely retry. When a worker receives SIGTERM it stops polling for new tasks and drains in-flight activities; if the platform sends SIGKILL before that finishes, those activities time out and retry. For idempotent work that costs you a duplicate execution and some latency. For non-idempotent work it is a data problem. Find the exact number for every platform on your shortlist — it is configurable on Kubernetes via terminationGracePeriodSeconds and on ECS via the stop timeout, and it is frequently short and undocumented on friendlier PaaS products. Then test it by deploying over a running activity.
Keep reading
- Best LangGraph deployment platforms in 2026 — The same split — durable state versus disposable compute — applied to agent graphs instead of workflows.
- Per-tenant isolation for a workflow engine — The deeper version of the customer-supplied-activity problem, and what a real isolation boundary costs.
- Running background workers alongside web apps — Why long-lived pollers and request handlers want different hosting, in general rather than Temporal-specific terms.
- Best managed Postgres providers in 2026 — If you do self-host the cluster, this is the database decision underneath it.
49ms p50 cold start. Fork, snapshot, and scale to zero.