How to Configure a Temporal Service without a Proxy
Introduction
There are many ways of deploying a Temporal Service. For a large-scale deployment, you can use Docker or Kubernetes to configure multiple pods with the ability to scale horizontally. For local development, you can use the server
subcommand of the Temporal CLI client to run a single-user server.
If you need a deployment that fits in between these options -- for example, if you need to scale for multiple users, with fine-grained control over your deployment parameters, but without the overhead of Kubernetes -- you can deploy a Temporal Service using the official server binaries.
In this tutorial, you'll configure and deploy the two binaries needed for a complete Temporal Service (the core server and the UI server). You'll create systemd
unit files to gracefully run and restart the Temporal Service automatically upon server startup, and you'll review additional configuration parameters for your Temporal Service. This will give you everything you need to run a production Temporal Service, and evaluate how to scale further or migrate to Temporal Cloud. Let's get started.
Prerequisites
- A Linux server with SSH access. This can be a new Ubuntu server instance with no additional configuration performed.
- To enable HTTPS in the browser, you will need SSL certificates and your own domain name pointing to the server.
Obtaining the Temporal Binaries
You'll begin by downloading and configuring the Temporal Server binaries.
The Temporal Core Server can be obtained from its Github Releases Page. The Temporal Core Server is responsible for orchestrating all tasks regarding the execution of Temporal Workflows, such as maintaining the Event History, maintaining Task Queues, responding to commands and more. Download the newest binary for your operating system (probably linux_amd64
) and extract it on the command line using curl
and tar
:
curl -OL https://github.com/temporalio/temporal/releases/download/v1.24.2/temporal_1.24.2_linux_amd64.tar.gz
tar -xzf temporal_1.24.2_linux_amd64.tar.gz
The extracted binary will be called temporal-server
. Move it to the /usr/bin/
directory on your path and make it executable:
sudo mv temporal-server /usr/bin/temporal-server
sudo chmod +x /usr/bin/temporal-server
You'll also need the Temporal UI server. The Temporal UI Server hooks in to a Temporal Core Server and provides a web-based UI for displaying information about Workflow Executions. It is a standalone binary that can also be obtained from its Github Releases page. Download the latest binary for your operating system and extract it on the command line using curl
and tar
:
curl -OL https://github.com/temporalio/ui-server/releases/download/v2.28.0/ui-server_2.28.0_linux_amd64.tar.gz
tar -xzf ui-server_2.28.0_linux_amd64.tar.gz
The extracted binary will be called ui-server
. Move it to the /usr/bin
directory on your path:
sudo mv ui-server /usr/bin/temporal-ui-server
sudo chmod +x /usr/bin/temporal-ui-server
At this point, you've downloaded everything you need. The last thing to do is create a temporal
user on your server that has the appropriate permissions to run the Temporal Service, and a directory accessible to this user to store your data in. Run the following commands:
sudo useradd temporal
sudo mkdir /etc/temporal
sudo chown temporal /etc/temporal
Next, you'll create configuration files for both the Temporal Server and the UI Server in the /etc/temporal/
directory.
Configuring the Temporal Binaries
To complete this step, you should have already obtained your own domain name and SSL certificates. One way to do that is by using certbot.
In this tutorial, you'll configure a connection to a SQLite database, since it doesn't require any additional dependencies. Using your favorite text editor, open a new file called /etc/temporal/temporal-server.yaml
:
sudo vim /etc/temporal/temporal-server.yaml
Paste the following contents into the file for a starting configuration. You can update any of these values later.
log:
stdout: true
level: info
persistence:
defaultStore: sqlite-default
visibilityStore: sqlite-visibility
numHistoryShards: 4
datastores:
sqlite-default:
sql:
pluginName: "sqlite"
databaseName: "/etc/temporal/default.db"
connectAddr: "localhost"
connectProtocol: "tcp"
connectAttributes:
cache: "private"
setup: true
sqlite-visibility:
sql:
pluginName: "sqlite"
databaseName: "/etc/temporal/visibility.db"
connectAddr: "localhost"
connectProtocol: "tcp"
connectAttributes:
cache: "private"
setup: true
global:
membership:
maxJoinDuration: 30s
broadcastAddress: "127.0.0.1"
pprof:
port: 7936
services:
frontend:
rpc:
grpcPort: 7233
membershipPort: 6933
bindOnIP: '0.0.0.0'
httpPort: 7243
matching:
rpc:
grpcPort: 7235
membershipPort: 6935
bindOnLocalHost: true
history:
rpc:
grpcPort: 7234
membershipPort: 6934
bindOnLocalHost: true
worker:
rpc:
membershipPort: 6939
clusterMetadata:
enableGlobalNamespace: false
failoverVersionIncrement: 10
masterClusterName: "active"
currentClusterName: "active"
clusterInformation:
active:
enabled: true
initialFailoverVersion: 1
rpcName: "frontend"
rpcAddress: YOUR_DOMAIN:PORT
httpAddress: "localhost:7243"
dcRedirectionPolicy:
policy: "noop"
Note YOUR_DOMAIN:PORT
in the rpcAddress
parameter. You should update this to reflect the URL that the Temporal gRPC API will be available on. You may use a subdomain like rpc.my_domain:7233
. If you use a port other than 7233, you should also update the grpcPort: 7233
parameter of the frontend service.
The gRPC API frontend configuration in this tutorial uses a default value of bindOnIP: '0.0.0.0'
, meaning that the Temporal API will be available globally, without authentication, to anyone who can access this server. This is generally only appropriate if you are otherwise controlling access to this server (e.g. through Kubernetes or by using an external proxy). If you need a self-contained access control solution, refer to our tutorials on Deploying Temporal with Nginx or Deploying Temporal with Envoy.
Temporal's gRPC API does not use TLS by default. Unlike HTTP/S connections, TLS is not always necessary for gRPC endpoints, depending on your security envelope. To configure TLS for your gRPC endpoint, refer to the Temporal documentation.
Save and close the file. Next, you'll create the configuration file for the UI Server. Using your favorite text editor, open a new file called /etc/temporal/temporal-ui-server.yaml
:
sudo vim /etc/temporal/temporal-ui-server.yaml
Paste the following contents into the file.
temporalGrpcAddress: 127.0.0.1:7233
host: YOUR_DOMAIN
port: 8233
enableUi: true
cors:
allowOrigins:
- http://YOUR_DOMAIN:8233
defaultNamespace: default
tls:
certFile: /etc/letsencrypt/live/YOUR_DOMAIN/cert.pem
keyFile: /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
enableHostVerification: true
If you are using your own domain name, replace YOUR_DOMAIN
in the host
and allowOrigins
.
To enable HTTPS, you also need to provide the paths to your certFile
and keyFile
. If you are using LetsEncrypt, these will be located in /etc/letsencrypt/live/YOUR_DOMAIN/cert.pem
and /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
. Make these changes, then save and close the file.
As with the gRPC API, this will make the Web UI available over HTTPS to anyone who can access this server -- ensure that you do not need a local proxy solution before proceeding.
You can now run a Temporal Service on this server by running the following commands in two separate terminals, to start the Core Server and the UI Server:
sudo su temporal -c `temporal-server -r / -c etc/temporal/ -e temporal-server start`
sudo su temporal -c `temporal-ui-server -r / -c etc/temporal/ -e temporal-ui-server start`
However, you aren't ready to handle external connections yet -- at this point, your Temporal Service is only available on localhost
, meaning it is not scalable or accessible outside the localhost network.
Use Ctrl+C
in each terminal to stop the running process, then delete the temporary DBs that were created:
sudo rm /etc/temporal/default.db
sudo rm /etc/temporal/visibility.db
In the remainder of this tutorial, you'll configure this server for production use.
Creating and Registering System Services
Because you installed Temporal directly from binaries, you need to run it manually from the command line. To run them automatically, you'll need to set up your own background services.
To do this, you’ll create unit
files that can be used by your server’s init
system. On nearly all modern Linux distributions, the init system is called systemd, and you can interact with it by using the systemctl
command.
Using your favorite text editor, open a new file called /etc/systemd/system/temporal.service
:
sudo vim /etc/systemd/system/temporal.service
Your unit file needs, at minimum, a [Unit]
section, a [Service]
section, and an [Install]
section:
[Unit]
Description=Temporal Service
After=network.target
[Service]
User=temporal
Group=temporal
ExecStart=temporal-server -r / -c etc/temporal/ -e temporal-server start
[Install]
WantedBy=multi-user.target
This file can be broken down as follows:
- The
[Unit]
section contains a plaintextDescription
of your new service, as well as anAfter
hook that specifies when it should be run at system startup, in this case, it will be run after your server’s networking interfaces have come up. - The
[Service]
section specifies which command (ExecStart
) should actually be run, as well as whichUser
andGroup
the command should be running as. In this case, you will use thetemporal
user you created, and thetemporal-server
command from the previous step. - The
[Install]
section contains only theWantedBy=multi-user.target
line, which works together with theAfter
line in the[Unit]
section to ensure that the service is started when the server is ready to accept user logins.
Save and close the file. You can now start
your new Temporal service, and enable
it to run on boot automatically:
sudo systemctl start temporal
sudo systemctl enable temporal
Use the systemctl
command to verify that temporal
started successfully. You should receive similar output to when you first ran the command in a terminal.
sudo systemctl status temporal
● temporal.service - Temporal Service
Loaded: loaded (/etc/systemd/system/temporal.service; disabled; vendo>
Active: active (running) since Mon 2024-07-08 11:24:40 PDT; 4s ago
Main PID: 19925 (temporal-server)
Tasks: 22 (limit: 18707)
Memory: 62.7M
CGroup: /system.slice/temporal.service
└─19925 temporal-server -r / -c etc/temporal/ -e temporal-server
Jul 08 11:24:42 Omelas temporal-server[19925]: {"level":"info","ts":"2024->
Jul 08 11:24:42 Omelas temporal-server[19925]: {"level":"info","ts":"2024->
Jul 08 11:24:42 Omelas temporal-server[19925]: {"level":"info","ts":"2024-
Next, repeat these steps for the UI server. Open a new file called /etc/systemd/system/temporal-ui.service
:
sudo vim /etc/systemd/system/temporal-ui.service
Add the following contents:
[Unit]
Description=Temporal UI Server
After=network.target
[Service]
User=temporal
Group=temporal
ExecStart=temporal-ui-server -r / -c etc/temporal/ -e temporal-ui-server start
[Install]
WantedBy=multi-user.target
Save and close the file, then start
the UI Server service, and enable
it to run on boot automatically:
sudo systemctl start temporal-ui
sudo systemctl enable temporal-ui
Use the systemctl
command to verify that temporal-ui
started successfully:
sudo systemctl status temporal-ui
Both services should now be running in the background. Navigate to YOUR_DOMAIN:8233 in a web browser, and you should receive the Temporal Web UI. You now have a working Temporal Service. In the next step, you'll review some additional configuration options.
Additional Configuration Options
Other Database Backends
This tutorial provides an example of using Temporal with a SQLite database backend. Temporal also supports MySQL, PostgreSQL, and Cassandra as database backends. Refer to the datasores documentation reference to make changes.
Load Balancing
You may have noticed that the temporal-server.yaml
configuration file that you edited earlier also contained several other port bindings -- for example, the History service and the Matching service. This is because most components of a Temporal Service can scale horizontally by adding additional nodes that can communicate and distribute load across a cluster.
In this tutorial, you deployed a single, standalone server binary. For more information about adding additional nodes, refer to Scaling Temporal: The Basics.
Visibility
This tutorial actually creates two different SQLite databases -- one for persisting your Workflow Event Histories, and another to act as a Visibility store. A Visibility store is required in a Temporal Service setup because it is used for querying and filtering your Workflows. Like your primary data store, your Visibility store can be configured to use a different database backend, and does not need to use the same configuration as your primary data store.
It is also possible to configure two Visibility stores, called Dual Visibility. This can be useful when preparing to migrate databases, or if your deployment is optimized to read from one database and write to another. Refer to How to set up Dual Visibility for more information.
Dev Ops
For any parameters not covered in this tutorial, refer to the Temporal documentation reference for both the Temporal Server (also referred to as a Temporal Cluster) and the Web UI. There are also dedicated documentation pages for several other self-hosting topics.
You can also refer to the Docker configuation template used by Temporal's Dockerhub images. If you are using Kubernetes, Temporal's helm-charts repo contains detailed documentation of the available options. You may also be interested in the Temporal Kubernetes Operator.
At this point, you're finished with configuration. In the final step, you'll review the logs generated by your Temporal Service, as well as your options for connecting to it from the Temporal CLI or SDK.
Interacting with the Temporal Service
You can use journalctl
to access logs from the Temporal Server. journalctl -u service-name.service
allows you to view the full logs of any service running through systemd
.
If you ever need to restart the Temporal Service after making a configuration change, use systemctl restart temporal
or systemctl restart temporal-ui
.
Finally, you should now be able to interact with your Temporal Service as if it were running locally. Just include --address your_server:7233
with your CLI commands as needed. The first thing you'll likely need to do is create a default
namespace, since this is not done automatically:
temporal --address your_server:7233 operator namespace create default
After that, you can visit the Web UI to ensure that it loads the default
namespace correctly by visting your_server
in a browser:
From then on, you can run commands like so:
temporal --address your_server:7233 workflow list
Refer to the Temporal documentation for more.
Conclusion
In this tutorial, you configured and deployed a baseline Temporal Service. Next, you can read about Temporal's Visiblity features which require adding ElasticSearch to your deployment. You can also learn more about the Temporal platform by following our self-paced online courses, or talk to an expert about Temporal Cloud.