Running Temporal Server on Minikube#
This guide deploys Temporal Server on a local Minikube cluster with PostgreSQL persistence. By the end, you will have the Temporal frontend, Web UI, and CLI all working against a real Kubernetes deployment.
If you need background on what Temporal is, start with Introduction to Temporal.
Prerequisites#
| Tool | Minimum Version | Purpose |
|---|---|---|
| minikube | 1.32+ | Local Kubernetes cluster |
| kubectl | 1.28+ | Kubernetes CLI |
| helm | 3.14+ | Package manager for Kubernetes |
| temporal | 1.0+ | Temporal CLI |
| docker | 24+ | Container runtime (minikube driver) |
Your machine needs at least 4 CPU cores and 8 GB RAM available to Docker. For minikube driver details, see Minikube Setup and Drivers and Minikube Docker Driver.
Quick Start with Makefile#
The companion repository handles everything:
git clone https://github.com/statherm/temporal-examples.git
cd temporal-examples
make cluster-up temporal-up
make temporal-statusThe rest of this article explains what those targets do.
Step-by-Step Setup#
Start Minikube#
minikube start \
--driver=docker \
--cpus=4 \
--memory=8192 \
--disk-size=20g \
--kubernetes-version=v1.29.2 \
--profile=temporalThe --profile=temporal flag creates a named cluster so it does not interfere with other minikube profiles.
Add Helm Repositories#
helm repo add temporal https://temporalio.github.io/helm-charts
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo updateInstall PostgreSQL#
Deploy PostgreSQL first so it is ready when Temporal starts:
kubectl create namespace temporal
helm upgrade --install temporal-postgresql bitnami/postgresql \
--namespace temporal \
--set auth.postgresPassword=temporal \
--set auth.database=temporal \
--set primary.persistence.size=5Gi \
--set primary.resources.requests.cpu=250m \
--set primary.resources.requests.memory=256Mi \
--wait --timeout 120sInstall Temporal Server#
Create a values file that points Temporal at your PostgreSQL instance and disables bundled databases:
# values-temporal.yaml
server:
replicaCount: 1
config:
persistence:
default:
driver: sql
sql:
driver: postgres12
host: temporal-postgresql
port: 5432
database: temporal
user: postgres
password: temporal
maxConns: 20
visibility:
driver: sql
sql:
driver: postgres12
host: temporal-postgresql
port: 5432
database: temporal_visibility
user: postgres
password: temporal
maxConns: 10
cassandra:
enabled: false
mysql:
enabled: false
postgresql:
enabled: false
elasticsearch:
enabled: false
schema:
setup:
enabled: true
update:
enabled: true
web:
replicaCount: 1
service:
type: ClusterIP
port: 8080We disable Cassandra, MySQL, and the bundled PostgreSQL because we deployed PostgreSQL separately. The schema.setup job creates required tables on first install.
helm upgrade --install temporal temporal/temporal \
--namespace temporal \
-f values-temporal.yaml \
--timeout 600sWait for Pods#
kubectl -n temporal rollout status deployment/temporal-frontend --timeout=300s
kubectl -n temporal rollout status deployment/temporal-history --timeout=300s
kubectl -n temporal rollout status deployment/temporal-matching --timeout=300s
kubectl -n temporal rollout status deployment/temporal-worker --timeout=300sHelm Values Walkthrough#
| Setting | Purpose |
|---|---|
server.replicaCount: 1 | Single replica per service. See Temporal HA Cluster for production. |
persistence.default.sql.driver: postgres12 | PostgreSQL 12+ wire protocol. Works with PostgreSQL 13-16. |
schema.setup.enabled: true | Run schema creation job on install. |
elasticsearch.enabled: false | Use SQL-based visibility. Simpler but less powerful for listing workflows. |
Accessing Temporal#
Port-forward the Web UI and frontend:
kubectl port-forward -n temporal svc/temporal-web 8080:8080 &
kubectl port-forward -n temporal svc/temporal-frontend 7233:7233 &Open http://localhost:8080 for the Web UI. Test the CLI:
temporal operator namespace list
temporal operator namespace create --namespace test-ns --retention 72hVerifying the Installation#
# Check all pods
kubectl get pods -n temporal
# Check cluster health
temporal operator cluster health
# SERVING
# Start a test workflow (no worker needed to verify server accepts it)
temporal workflow start \
--task-queue test-queue \
--type HelloWorkflow \
--workflow-id test-001
temporal workflow describe --workflow-id test-001The workflow stays “Running” because no worker is registered. See Your First Temporal Workflow in Go to build one.
Teardown#
# Quick (from companion repo)
make cluster-down
# Manual
helm uninstall temporal -n temporal
helm uninstall temporal-postgresql -n temporal
kubectl delete pvc --all -n temporal
kubectl delete namespace temporal
minikube delete --profile=temporalTroubleshooting#
CrashLoopBackOff on server pods. Usually the schema job did not complete. Check kubectl logs -n temporal job/temporal-schema-setup. If PostgreSQL was not ready, delete the jobs and reinstall:
kubectl delete job -n temporal temporal-schema-setup temporal-schema-update
helm upgrade --install temporal temporal/temporal -n temporal -f values-temporal.yamlPostgreSQL connection refused. Verify the service name matches your values file: kubectl get svc -n temporal | grep postgresql. The Bitnami chart names the service after the release name directly (e.g., temporal-postgresql, not temporal-postgresql-postgresql).
Pods stuck in Pending. Insufficient resources. Increase minikube allocation:
minikube stop --profile=temporal
minikube start --profile=temporal --cpus=6 --memory=10240Web UI shows no data. Check frontend logs for gRPC or persistence errors: kubectl logs -n temporal deployment/temporal-frontend --tail=50.
Next Steps#
- Temporal HA Cluster – multi-replica production deployment
- Namespaces and Task Queues – organize workflows with proper isolation
- Your First Temporal Workflow in Go – write, build, and run a real workflow