Skip to main content

Deploy a TypeScript application to production on a Digital Ocean Droplet using Temporal Cloud

Introduction

When you are ready to deploy your TypeScript based Temporal Application to production, you need a server to host it. Digital Ocean provides flexible Cloud servers called Droplets that can be used to host your application.

In this tutorial we will walk through the steps to deploy your Temporal Application to a Digital Ocean Droplet while using Temporal Cloud as your Temporal Application's orchestrator.

Prerequisites

Before you begin, you will need the following:

  • A Digital Ocean account: You need a Digital Ocean account to create a Droplet.
  • A domain name: You need a domain, because you need to have a valid SSL certificate for the Temporal Application to communicate with Temporal Cloud.
  • A Temporal Cloud account: You need a Temporal Cloud account to generate a certificate for your Temporal Application to communicate with Temporal Cloud.
  • A Temporal Application that you want to deploy: You need a TypeScript based Temporal Application that you want to deploy to a Digital Ocean Droplet.

Step 1 -- Create and set up Droplet

Create a new Droplet and choose the Ubuntu 20.04 image or Ubuntu 22.04 image.

Then, set up the following on your Droplet:

  • Configure SSH
  • Install Node.js
  • Install TypeScript and TS Node
  • Set up Nginx as a reverse proxy
  • Create a domain certificate

Set up SSH

Next, set up SSH so you can set up your server from your local terminal. You can do that by following the Add SSH Key to Droplet tutorial.

If you already have a private key on your local machine and you need to force SSH to use the new key, you can force it with the -i flag, for example:

ssh -i ~/.ssh/id_rsa_digitalocean

Install Node.js

Since you are going to be deploying a TypeScript application, you need to install TypeScript into this environment. First, install Node.js, a JavaScript runtime for server-side programming.

There are several options for installing Node.js. The most straightforward is to install from your system package manager. For example, on Ubuntu:

sudo apt update
sudo apt install nodejs

This will install the newest stable version from Ubuntu sources. If you need to install a different version of Node.js for compatibility reasons, refer to these tutorials respectively:

Install TypeScript and TS Node

Next, use the Node.js package manager, npm, to install some packages that provide TypeScript compatibility. Install the typescript and ts-node packages globally, using the -g flag:

sudo npm install -g typescript ts-node

Update your domain's DNS

Ensure the domain's A record points to the droplet's IP address.

Install and Configure Nginx as a Reverse Proxy

Because you'll be deploying an application that handles external requests over HTTP/S, we strongly recommend also deploying an Nginx reverse proxy.

Node.js applications typically bind to localhost. This means by default, your application will only be accessible locally on the machine it resides on. While you can specify a different port to allow access through the internet, Node.js's built in application server provides fairly minimal security and configurability. Using a reverse proxy isolates the application server from direct internet access, allows you to centralize firewall protection, and minimizes the attack surface for common threats such as denial of service attacks.

From a client’s perspective, interacting with a reverse proxy is no different from interacting with the application server directly. It is functionally the same, and the client cannot tell the difference. A client requests a resource and then receives it, without any extra configuration required by the client. Nginx, a popular open source web server, can act as a reverse proxy with a nearly drop-in configuration.

Follow these tutorials, for Ubuntu 20.04 or 22.04 respectively, including setting up the server blocks:

Create a domain certificate

Use Let's encrypt certbot to create a certificate for your domain. This will effectively enable HTTPS for your domain and your Digital Ocean Droplet.

Follow the tutorial for your Ubuntu version:

Step 2 -- Create a Temporal Cloud certificate

You will need a seperate certificate to authenticate the communication between your Temporal Client, Temporal Workers, and Temporal Cloud.

You can use tcld to generate the certificate.

Follow the steps to install tcld.

Then, follow the steps to generate a Temporal Cloud certificate using tcld.

Step 3 -- Configure your app on the Droplet

Now that you have your Droplet set up, and your Temporal Cloud certificates, you can set up your Temporal Application on the Droplet.

Clone the repo

Using Git, you can clone the droplet and install the dependencies.

git clone <your-repo>
cd <your-repo>/temporal-application
npm install
cd ..
cd <your-repo>/bot
npm install

Populate your .env file

Per the 12 Factor App methodology, you should store your credentials in environment variables.

Create a .env file in the root of any project that uses a Temporal Client or runs a Temporal Worker. Then, add your Temporal Cloud Namespace, address, certificate PEM and KEY to the .env file.

TEMPORAL_CLOUD_ADDRESS=""
TEMPORAL_CLOUD_NAMESPACE=""
TEMPORAL_CLOUD_PEM=""
TEMPORAL_CLOUD_PRIVATE_KEY=""

Your Temporal Client and Worker will need those environment variables to connect to Temporal Cloud.

Use Temporal Cloud connection details

Connecting to Temporal Cloud requires a few changes to your Temporal Client and Worker, using the environment variables you set in the .env file.

Temporal Client

When developing locally, your Temporal Client may look like this:

docs/tutorials/typescript/work-queue-slack-app/code/bot/modules/dev-temporal-client.ts

import "dotenv/config";
import {Client, Connection} from "@temporalio/client";

export let temporalClient: Client;

export async function initializeTemporalClient() {
const connection = await Connection.connect();

temporalClient = new Client({
connection,
namespace: process.env.TEMPORAL_DEV_NAMESPACE!,
});
}

When using Temporal Cloud, your Temporal Client should look like this:

docs/tutorials/typescript/work-queue-slack-app/code/bot/modules/cloud-temporal-client.ts

import "dotenv/config";
import {Client, Connection} from "@temporalio/client";

export let temporalClient: Client;

export async function initializeTemporalClient() {
const key = Buffer.from(process.env.TEMPORAL_CLOUD_PRIVATE_KEY!, "utf-8");
const cert = Buffer.from(process.env.TEMPORAL_CLOUD_PEM!, "utf-8");
const address = process.env.TEMPORAL_CLOUD_ADDRESS!;
const namespace = process.env.TEMPORAL_CLOUD_NAMESPACE!;
const connection = await Connection.connect({
address: address,
tls: {
clientCertPair: {
crt: cert,
key: key,
},
},
});

temporalClient = new Client({
connection,
namespace: namespace,
});
}

Temporal Worker

When developing locally, your Temporal Worker may look like this:

docs/tutorials/typescript/work-queue-slack-app/code/temporal-application/src/dev-worker.ts

import "dotenv/config";
import path from "path";
import {Worker, NativeConnection} from "@temporalio/worker";
import * as activities from "./activities/index";

async function run() {
try {
const worker = await Worker.create({
namespace: process.env.TEMPORAL_DEV_NAMESPACE || "",
workflowsPath: path.resolve(__dirname, "./workflows"),
activities,
taskQueue: `${process.env.ENV}-temporal-iq-task-queue`,
});

await worker.run();
} catch (err) {
console.error(err);
process.exit(1);
}
}

run();

When using Temporal Cloud, your Temporal Client should look like this:

docs/tutorials/typescript/work-queue-slack-app/code/temporal-application/src/cloud-worker.ts

import "dotenv/config";
import path from "path";
import {Worker, NativeConnection} from "@temporalio/worker";
import * as activities from "./activities/index";

async function run() {
try {
const key = Buffer.from(
process.env.TEMPORAL_CLOUD_PRIVATE_KEY || "",
"utf-8"
);
const cert = Buffer.from(process.env.TEMPORAL_CLOUD_PEM || "", "utf-8");
const connection = await NativeConnection.connect({
address: process.env.TEMPORAL_CLOUD_ADDRESS || "",
tls: {
clientCertPair: {
crt: cert,
key: key,
},
},
});

const worker = await Worker.create({
connection,
namespace: process.env.TEMPORAL_CLOUD_NAMESPACE || "",
workflowsPath: path.resolve(__dirname, "./workflows"),
activities,
taskQueue: `${process.env.ENV}-temporal-iq-task-queue`,
});

await worker.run();
} catch (err) {
console.error(err);
process.exit(1);
}
}

run();

Step 4 -- Use pm2 to run your Worker

pm2 is a process manager for Node.js applications, and will ensure that your TypeScript application runs continuously on your Droplet.

sudo npm install -g pm2

Change directory into your project and run your Temporal Worker with pm2.

pm2 start <your-app-entry>.ts --interpreter ts-node

Conclusion

You should now have a TypeScript based Temporal Application running on a Digital Ocean Droplet using Temporal Cloud as your Temporal Application's orchestrator.