tutorial

How to Deploy Temporal Self-Hosted on a Single Server in 2026

A step-by-step tutorial for self-hosting the open-source Temporal Server on a single Linux server using Docker Compose. Covers cluster bring-up, namespace registration, worker deployment, security hardening, and scaling caveats. Suitable for development environments and low-volume production workloads up to approximately 100 workflow executions per second.

Overview

Temporal is a durable workflow engine that orchestrates long-running, fault-tolerant business processes. While Temporal Cloud provides a fully managed offering, many teams prefer to self-host the open-source Temporal Server for cost control, data residency, or compliance reasons. This tutorial walks through deploying Temporal on a single Linux server using Docker Compose, suitable for development environments and small production workloads.

Prerequisites

  • A Linux server with at least 4 GB RAM and 2 vCPU (Ubuntu 22.04 LTS recommended)
  • Docker Engine 24.0+ and Docker Compose v2 installed
  • A non-root user with sudo and Docker group membership
  • Inbound firewall rules permitting access on port 7233 (gRPC) and 8080 (Web UI), restricted to trusted IP ranges

Architecture

A minimal self-hosted Temporal stack consists of four containers:

  1. temporal — The Temporal Server (history, matching, frontend, worker services bundled in auto-setup mode)
  2. postgresql — Persistence backend for workflow state (alternative: MySQL or Cassandra)
  3. temporal-admin-toolstctl and temporal CLI utilities
  4. temporal-ui — Web UI for inspecting workflow executions

Step 1: Create the docker-compose.yml

Create a directory /opt/temporal and inside it a docker-compose.yml:

version: "3.5"
services:
  postgresql:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: temporal
      POSTGRES_USER: temporal
    volumes:
      - temporal-pg:/var/lib/postgresql/data
    restart: unless-stopped

  temporal:
    image: temporalio/auto-setup:1.24
    environment:
      DB: postgres12
      DB_PORT: 5432
      POSTGRES_USER: temporal
      POSTGRES_PWD: temporal
      POSTGRES_SEEDS: postgresql
    depends_on:
      - postgresql
    ports:
      - "7233:7233"
    restart: unless-stopped

  temporal-admin-tools:
    image: temporalio/admin-tools:1.24
    depends_on:
      - temporal
    stdin_open: true
    tty: true

  temporal-ui:
    image: temporalio/ui:2.27.0
    environment:
      TEMPORAL_ADDRESS: temporal:7233
      TEMPORAL_CORS_ORIGINS: http://localhost:3000
    depends_on:
      - temporal
    ports:
      - "8080:8080"
    restart: unless-stopped

volumes:
  temporal-pg:

Pin image tags rather than using latest so that future restarts do not silently upgrade the cluster.

Step 2: Start the Cluster

cd /opt/temporal
docker compose up -d
docker compose logs -f temporal

Wait until log output stabilises with messages such as Frontend service started and no further errors. Initial schema setup typically takes 30-60 seconds.

Step 3: Verify the Cluster

From the host:

docker compose exec temporal-admin-tools temporal operator cluster health

A healthy cluster returns SERVING. Open http://<server-ip>:8080 in a browser to confirm the Web UI loads. Restrict this port to a VPN or office IP range; the UI ships without authentication by default.

Step 4: Register a Namespace

Each application runs inside a namespace. Create one for the first application:

docker compose exec temporal-admin-tools \
  temporal operator namespace create --namespace=default --retention=7d

Retention controls how long completed workflow histories are kept. Seven days is appropriate for most non-regulated workloads.

Step 5: Deploy a Worker

Workers are application processes that poll Temporal for tasks. They run alongside the application code in any supported SDK (Go, Java, TypeScript, Python, .NET, PHP). A minimal Python worker:

from temporalio.client import Client
from temporalio.worker import Worker
import asyncio

async def main():
    client = await Client.connect("temporal:7233", namespace="default")
    worker = Worker(client, task_queue="my-task-queue", workflows=[], activities=[])
    await worker.run()

asyncio.run(main())

Run the worker as a separate container or systemd unit. Workers connect outbound to port 7233; they do not need to be exposed externally.

Security Considerations

  • Authentication: The default Temporal Server has no auth. For production, configure mTLS using the --tls-cert-path and --tls-key-path flags or front the gRPC port with an authenticating proxy.
  • Web UI: The UI is unauthenticated by default. Restrict access via VPN, place behind an authenticating reverse proxy (Caddy with basic auth, Cloudflare Access), or build a custom OIDC integration.
  • Database backups: Temporal stores all workflow state in PostgreSQL. Daily pg_dump backups (or WAL archiving) are essential; losing the database loses all workflow history.
  • Encryption: Use the data converter pattern in the SDK to encrypt sensitive payloads before they reach the server.

Scaling Caveats

A single-server deployment is suitable for development and workloads under approximately 100 workflow executions per second. Production deployments at higher throughput should:

  • Run separate frontend, history, matching, and worker services rather than the bundled auto-setup image
  • Use a managed PostgreSQL cluster (RDS, Cloud SQL) or migrate to Cassandra for horizontal write scaling
  • Place workers in a separate cluster from the Temporal Server cluster
  • Monitor service_pending_requests, persistence_latency, and workflow_task_schedule_to_start_latency Prometheus metrics

Common Errors

  • connection refused on port 7233 — server not yet ready or DB schema setup still running
  • namespace not found — register the namespace before connecting workers
  • task queue not configured — workers must be running and polling the same task queue the workflow targets

Operating Cost

A self-hosted Temporal cluster on a single Hetzner CCX13 (4 vCPU, 16 GB RAM, approximately €15/month as of 2026) handles low-volume production workloads. Compared to Temporal Cloud (starting at $200/month minimum), self-hosting is cheaper at small scale; at high scale the operational overhead typically outweighs savings.

Editor's Note: We deployed this stack at ShadowGen for an internal automation platform handling roughly 8,000 workflow executions per day. Hardware: a single Hetzner CCX23 (8 vCPU, 32 GB RAM) at approximately €30/month. Total deployment time: 2 hours including TLS configuration via a Caddy reverse proxy. The biggest gotcha was DB backup discipline — the first month we ran without WAL archiving and a single docker compose down -v would have wiped 30 days of workflow history. Since adding pgBackRest with hourly archives, recovery testing has been clean. Caveat: at this scale the operational cost (monitoring, upgrades, schema migrations during minor version bumps) is roughly 4-6 engineer-hours per month, which exceeds the licence savings versus Temporal Cloud Starter for some teams.

Written & reviewed by Rafal Fila · Last updated:

Tools Mentioned

Related Guides

comparison

Keystroke vs n8n in 2026: Agent-Built TypeScript vs the Visual Canvas

Keystroke, launched in July 2026 by Y Combinator W24 company Sprint Labs, is a code-first automation platform where AI coding agents write workflows as TypeScript in the user's repository. n8n, founded in 2019, is the most widely deployed source-available visual workflow platform, with 200,000+ users and a $2.5 billion valuation. This comparison covers the agent-authored versus canvas building models, durable execution, licensing (Elastic License 2.0 vs the Sustainable Use License), verified July 2026 pricing including Keystroke's usage metering, and the maturity gap between a days-old platform and an established ecosystem.

comparison

QuantumBPM vs Camunda 2026: Single-Binary Challenger vs the BPMN Incumbent

QuantumBPM (launched 2026, Coroid s.r.o., Slovakia) packages a BPMN 2.0 runtime and DMN 1.5 decision engine into one Go binary backed by Temporal and PostgreSQL. Camunda (Berlin, founded 2013) is the category incumbent: Camunda 7 (Apache 2.0, in maintenance) and the Zeebe-based Camunda 8 platform. This comparison covers product structure, architecture, DMN TCK conformance with recording dates, deployment, pricing, and vendor maturity, verified July 2026.

case-study

Migrating 23 Make Scenarios to Self-Hosted n8n: a 3-Week Breakdown

Anonymized retrospective of a DTC ecommerce brand migrating 23 Make scenarios to a self-hosted n8n instance over three weeks. Tooling cost dropped from $348/month on Make Teams to roughly $12/month on a Hetzner VPS, but credential and webhook recreation consumed about 40% of total project time.

Related Rankings

Common Questions

Is Keystroke worth it in 2026?

Keystroke earns a provisional 6.5/10 in its July 2026 open alpha: the agent-native TypeScript model is genuinely differentiated and the free Hobby tier makes it safe to trial, but the platform is pre-1.0 (npm at v0.1.98, public repository published July 13, 2026), has no third-party production track record, and its Elastic License 2.0 restricts offering it as a hosted service. Worth trialing for teams that build through coding agents; too young for production-critical workflows.

What is Keystroke?

Keystroke is a code-first workflow automation and AI agent platform, launched July 13, 2026 by Y Combinator-backed Sprint Labs, that positions itself as an n8n alternative built for AI coding agents. Workflows are written as typed TypeScript in the user's own repository, usually by agents such as Claude Code, Cursor, or Codex, and deployed to Keystroke's managed cloud or self-hosted under the source-available Elastic License 2.0.

How much does Keystroke cost in 2026?

Keystroke offers three tiers as of July 2026: Hobby (free forever, with $1/month of included usage credit), Pro ($20/month, including $20/month of usage credit), and Organization (custom pricing with SSO, RBAC, and audit logs). Usage is metered on every tier: $0.01 per agent or workflow run, $0.005 per empty poll, $0.007 per web search, roughly $0.067 per hour of sandbox compute, and a 1.1x markup on AI model calls unless you bring your own API keys.

Is Keystroke a good n8n alternative?

Keystroke is a credible n8n alternative only for teams that build automations through coding agents such as Claude Code or Cursor, and even then as an experiment rather than a replacement as of July 2026: it launched July 13, 2026 and is pre-1.0, while n8n has 200,000+ users, 196,000+ GitHub stars, and a mature self-hosting story. For visual building, non-developer operators, or production-critical workflows, n8n remains the stronger choice.