Complete Temporal environment with PostgreSQL, Casdoor (OIDC), and UI.
| Service | Port | URL |
|---|---|---|
| Temporal Server | 7233 | localhost:7233 |
| Temporal UI | 8080 | http://localhost:8080 |
| Casdoor (OIDC) | 8000 | http://localhost:8000 |
| PostgreSQL | 5432 | localhost:5432 |
- Copy the example file and adjust if needed:
cp .env.example .env
# Edit .env to change TEMPORAL_HOST if you need to access via IP- Start the services:
docker compose up -dWait for all services to become healthy:
docker ps --format "table {{.Names}}\t{{.Status}}"Run the setup script:
./scripts/setup-casdoor.shThe script automatically creates:
- Organization
temporal - Application
temporal-uiwith Client ID/Secret - Test user
testuserwith passwordTemporal123!
Access Casdoor Admin UI and configure manually.
- User: admin
- Password: 123
- Organization: built-in
- Go to Organizations → Add
- Fill in:
- Name:
temporal - DisplayName: Temporal
- Website:
http://localhost:8080
- Name:
- Click Save
- Go to Applications → Add
- Fill in:
- Organization: temporal
- Name:
temporal-ui - Client ID:
temporal-ui - Client secret:
temporal-ui-secret - Redirect URLs:
http://localhost:8080/auth/callbackhttp://localhost:8080
- Token format: JWT
- Expire in hours: 168 (7 days)
- Grant types: authorization_code, refresh_token
- Click Save
- Go to Users → Add
- Fill in:
- Organization: temporal
- Name:
testuser - Password:
Temporal123! - Email:
testuser@temporal.local - Email verified: true
- Click Save
- Access http://localhost:8080
- You will be redirected to Casdoor
- Login with:
- Organization: temporal
- Username: testuser
- Password:
Temporal123!
- After login, you will be redirected back to the UI
The default namespace is configured with:
- Retention: 720h (30 days)
- History Archival: Enabled (
file:///tmp/temporal_archival/development) - Visibility Archival: Enabled (
file:///tmp/temporal_vis_archival/development)
docker exec temporal-server temporal operator namespace describe default# Enable archival (already done by setup)
docker exec temporal-server temporal operator namespace update default \
--history-archival-state enabled \
--visibility-archival-state enabled \
--retention 720h# Without auth (server without auth enabled)
temporal workflow list --address localhost:7233
# List namespaces
temporal operator namespace list --address localhost:7233
# Execute test workflow
temporal workflow execute \
--address localhost:7233 \
--namespace default \
--task-queue test \
--type test \
--input '"hello"'import "go.temporal.io/sdk/client"
func main() {
c, err := client.Dial(client.Options{
HostPort: "localhost:7233",
Namespace: "default",
})
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Use client...
}from temporalio.client import Client
async def main():
client = await Client.connect(
"localhost:7233",
namespace="default"
)
# Use client...import { Client } from '@temporalio/client';
const client = new Client({
address: 'localhost:7233',
namespace: 'default',
});
// Use client...Archived workflows are stored in Docker volumes:
- History:
archive_data→/tmp/temporal_archival - Visibility:
archive_vis_data→/tmp/temporal_vis_archival
# List files in container
docker exec temporal-server ls -la /tmp/temporal_archival/development/
docker exec temporal-server ls -la /tmp/temporal_vis_archival/development/To use MinIO as S3 backend:
- Uncomment
minioandminio-initsections indocker-compose.yml - Update
dynamicconfig/docker.yaml:
history.archival:
- value:
enableReadFromArchival: true
enableWriteToArchival: true
uri: "s3://temporal-archive/"
s3:
endpoint: "minio:9000"
accessKey: "temporal"
secretKey: "temporal_archive_secret"
region: "us-east-1"
usePathStyleAccess: trueTo generate a JWT token manually (for CLI testing):
./scripts/generate-jwt.shThe token will be saved to config/jwt/token.txt.
export TEMPORAL_CLI_AUTH_TOKEN=$(cat config/jwt/token.txt)
temporal workflow list --address localhost:7233┌─────────────────────────────────────────────────────────────┐
│ Docker Network │
│ temporal-network │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐│
│ │ PostgreSQL │ │ Casdoor │ │ Temporal Server ││
│ │ :5432 │ │ :8000 │ │ :7233 ││
│ │ (database) │ │ (OIDC) │ │ (auto-setup) ││
│ └──────────────┘ └──────────────┘ └──────────────────────┘│
│ ┌──────────────────┤│
│ │ Temporal UI ││
│ │ :8080 ││
│ │ (OIDC client) ││
│ └──────────────────┘│
└─────────────────────────────────────────────────────────────┘
- Check if Auth is enabled:
curl http://localhost:8080/api/v1/settings | jq .Auth - Check logs:
docker logs temporal-ui - Restart UI:
docker compose restart temporal-ui
- Check if Casdoor is accessible:
curl http://localhost:8000/.well-known/openid-configuration
- Check if the application exists in Casdoor
- Verify Client ID/Secret are correct
- Check if
casdoordatabase was created:docker exec temporal-postgres psql -U postgres -l | grep casdoor
- If not, create it manually:
docker exec temporal-postgres psql -U postgres -c "CREATE DATABASE casdoor;"
- Check logs:
docker logs temporal-server 2>&1 | tail -50
- Common issues:
- Database not ready: wait longer
- Invalid JWT key source: check Casdoor URL
- Auth enabled without complete configuration: disable auth on server
- Check if archival is enabled on the namespace
- Check if the workflow was closed longer than retention time
- Check archival files in the container
To enable authentication on the Temporal server (not just UI):
- Update
dynamicconfig/docker.yaml:
frontend.auth:
- value:
jwtKeySource:
- keySourceKind: "jwks"
url: "http://casdoor:8000/.well-known/jwks"
claimMapper:
name: "jwt"
authorizer:
name: "default"- Add to
docker-compose.yml:
environment:
- TEMPORAL_JWT_KEY_SOURCE1=http://casdoor:8000/.well-known/jwksWarning: This may block internal system workflows. Use with caution.
# Create production namespace
temporal operator namespace create production \
--retention 720h \
--history-archival-state enabled \
--visibility-archival-state enabled# Backup
docker exec temporal-postgres pg_dump -U temporal temporal > backup_temporal.sql
docker exec temporal-postgres pg_dump -U temporal temporal_visibility > backup_visibility.sql
# Restore
cat backup_temporal.sql | docker exec -i temporal-postgres psql -U temporal
cat backup_visibility.sql | docker exec -i temporal-postgres psql -U temporalMIT
By default, all services use localhost. To access from another machine on the network:
-
Copy the environment file:
cp .env.example .env
-
Edit
.envand changeTEMPORAL_HOST:TEMPORAL_HOST=192.168.1.100
-
Restart services:
docker compose down && docker compose up -d
All URLs (OIDC, callbacks, CORS) will be automatically adjusted.