·6 min read

Temporal.io: Workflow Orchestration That Survives Restarts

Why I run Temporal on my homelab — and how durable workflow execution changes the way you think about long-running background jobs.

AutomationBackendDockerArchitecture

Cron jobs and message queues solve most scheduling problems, but they share a common failure mode: if the process crashes mid-execution, the state is gone. You either re-run the whole job (and handle idempotency) or accept partial work. Temporal solves this by making workflow state durable — a workflow that crashes mid-step resumes from exactly where it left off after a restart.

The core idea

A Temporal workflow is just code — a function in any supported language (Go, TypeScript, Python, Java, .NET). But instead of running in memory, every state transition is written to a durable event log. If the worker process dies, a new worker picks up the workflow, replays the event log to restore state, and continues from the last committed step. Your code sees none of this — it looks like the function never stopped.

typescript
// A workflow that survives restarts mid-execution
import { proxyActivities, sleep } from "@temporalio/workflow";

const { sendEmail, processPayment, updateInventory } = proxyActivities({
  startToCloseTimeout: "5 minutes",
});

export async function orderWorkflow(orderId: string) {
  await processPayment(orderId);    // if worker dies here...
  await sleep("10 seconds");
  await updateInventory(orderId);   // ...it resumes here after restart
  await sendEmail(orderId, "confirmed");
}

When you actually need this

  • Multi-step processes that span minutes or hours (order fulfillment, data pipelines)
  • Human-in-the-loop flows that wait for approval before continuing
  • Retry logic with complex backoff that you don't want to implement yourself
  • Workflows that call external APIs and need to handle partial failures gracefully
  • Replacing fragile cron + database flag patterns

Self-hosting with Docker

Temporal's self-hosted stack is: the Temporal server, a PostgreSQL database for workflow state, Elasticsearch for visibility/search, and the admin-tools container for CLI access. The auto-setup image handles schema migration on first start. Workers are separate services you write — they poll Temporal for tasks and execute your workflow and activity code.

bash
# Check namespace list
docker exec temporal-admin-tools tctl namespace list

# List running workflows
docker exec temporal-admin-tools tctl workflow list

# Describe a specific workflow
docker exec temporal-admin-tools tctl workflow describe --workflow_id your-id
Note:For personal homelab use, the default single-node setup with PostgreSQL and basic visibility (no Elasticsearch) is sufficient. Elasticsearch adds workflow search but adds significant memory overhead.

What it replaces in practice

I've replaced several cron + bash script patterns with Temporal workflows: nightly backup verification that chains multiple steps, a media sync workflow that retries on network failures, and an alert escalation flow that waits for acknowledgment before notifying the next person. The visibility UI alone (seeing exactly which step a workflow is on, its history, input/output per step) pays for the operational overhead of running the stack.