Skip to main content

Embedding Temporal server as a Go library

View Markdown

You can run Temporal server as an embedded Go library instead of deploying it as a separate service. This approach is useful for testing and development scenarios where you want to run Temporal in-process without managing external infrastructure.

Not for production use

Embedded deployments with SQLite are suitable for testing and development only. For production workloads, deploy Temporal as a service using MySQL, PostgreSQL, or Cassandra as the persistence layer.

Reference implementation​

The recommended way to run an embedded Temporal server is to use the Temporal CLI's dev server implementation as a reference. The CLI's devserver package provides a complete implementation that handles:

  • SQLite configuration and schema setup
  • Namespace creation
  • Service configuration
  • Port allocation

You can study and adapt this implementation for your own embedded use case.

Basic server API​

The core API for embedding Temporal is temporal.NewServer():

import (
"go.temporal.io/server/temporal"
"go.temporal.io/server/common/config"
)

server, err := temporal.NewServer(
temporal.ForServices(temporal.DefaultServices),
temporal.WithConfig(cfg),
temporal.InterruptOn(temporal.InterruptCh()),
)
if err != nil {
log.Fatal(err)
}

if err := server.Start(); err != nil {
log.Fatal(err)
}

The challenge is building the config.Config struct correctly, especially for SQLite which requires:

  1. Schema setup - SQLite databases need schema initialization via sqliteschema.SetupSchema()
  2. Namespace creation - Namespaces can be pre-created via sqliteschema.CreateNamespaces()
  3. Service configuration - All four services (frontend, history, matching, worker) need proper port configuration

Configuration from file​

For non-SQLite databases, you can load configuration from a YAML file:

cfg, err := config.Load(
config.WithConfigFile("/path/to/config.yaml"),
)
if err != nil {
log.Fatal(err)
}

server, err := temporal.NewServer(
temporal.ForServices(temporal.DefaultServices),
temporal.WithConfig(cfg),
)

Or load from a directory with environment-specific files:

cfg, err := config.Load(
config.WithConfigDir("./config"),
config.WithEnv("development"),
)

Server options reference​

The temporal.NewServer() function accepts options to customize the server. See Server Options Reference for the complete list.

Key options include:

OptionDescription
ForServices([]string)Services to run (default: frontend, history, matching, worker)
WithConfig(*config.Config)Server configuration
WithLogger(log.Logger)Custom logger
WithAuthorizer(authorization.Authorizer)Custom authorization
WithClaimMapper(func)Role/claim mapping for auth
WithCustomMetricsHandler(metrics.Handler)Custom metrics handler
WithDynamicConfigClient(dynamicconfig.Client)Runtime configuration
InterruptOn(chan)Channel for graceful shutdown

SQLite limitations​

SQLite is intended for testing and development only:

  • Single writer: SQLite supports only one writer at a time, limiting write throughput
  • No durability in memory mode: In-memory mode loses data on restart
  • Not scalable: Cannot handle production workloads
  • Single shard: Use NumHistoryShards: 1 for SQLite

For production, use MySQL, PostgreSQL, or Cassandra with a properly scaled multi-node deployment.

Examples​

For complete working examples, see: