<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tutorials on cache cascade</title><link>https://blog.taigrr.com/categories/tutorials/</link><description>Recent content in Tutorials on cache cascade</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>tai@taigrr.com (Tai Groot)</managingEditor><webMaster>tai@taigrr.com (Tai Groot)</webMaster><lastBuildDate>Sun, 18 May 2025 22:31:25 +0600</lastBuildDate><atom:link href="https://blog.taigrr.com/categories/tutorials/index.xml" rel="self" type="application/rss+xml"/><item><title>Using PAM for SSH Login Telephony</title><link>https://blog.taigrr.com/blog/pam-telephony/</link><pubDate>Sun, 18 May 2025 22:31:25 +0600</pubDate><author>tai@taigrr.com (Tai Groot)</author><guid>https://blog.taigrr.com/blog/pam-telephony/</guid><description>&lt;h1 id="real-time-ssh-login-alerts-using-pam-and-gotify"&gt;Real-Time SSH Login Alerts Using PAM and Gotify&lt;/h1&gt;
&lt;p&gt;Pluggable Authentication Modules (
&lt;a
href="https://github.com/linux-pam/linux-pam"
target="_blank"
&gt;PAM&lt;/a
&gt;) provide a flexible mechanism for authenticating users on Linux systems.
By integrating PAM with Gotify, you can receive real-time notifications for SSH login attempts, enhancing your system&amp;rsquo;s security and awareness.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using this approach for over 5 years across many systems — it&amp;rsquo;s lightweight, reliable, and highly effective.
Let&amp;rsquo;s walk through how to set it up.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before you begin, make sure you have the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A Linux server with SSH access.&lt;/li&gt;
&lt;li&gt;Root or &lt;code&gt;sudo&lt;/code&gt; privileges.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curl&lt;/code&gt; installed.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="step-1-install-and-configure-gotify"&gt;Step 1: Install and Configure Gotify&lt;/h2&gt;
&lt;p&gt;
&lt;a
href="https://gotify.net/"
target="_blank"
&gt;Gotify&lt;/a
&gt; is a simple self-hosted server for sending notifications. You can also use a hosted instance if desired.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Gotify or use an existing instance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create an application in Gotify and note down the application token.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test that your setup works by sending a test message:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X POST &lt;span style="color:#e6db74"&gt;&amp;#34;http://&amp;lt;gotify-server&amp;gt;/message&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-Gotify-Key: &amp;lt;application-token&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -F &lt;span style="color:#e6db74"&gt;&amp;#34;title=Test Notification&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -F &lt;span style="color:#e6db74"&gt;&amp;#34;message=This is a test message&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="step-2-create-the-pam-notification-script"&gt;Step 2: Create the PAM Notification Script&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll configure PAM to trigger this script every time someone logs in via SSH.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo vim /usr/local/bin/pam-ssh-notify.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Paste the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GOTIFY_URL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;http://&amp;lt;gotify-server&amp;gt;/message&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GOTIFY_TOKEN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;gotify-token&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/bin/curl -s -X POST &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$GOTIFY_URL&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-Gotify-Key: &lt;/span&gt;$GOTIFY_TOKEN&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -F &lt;span style="color:#e6db74"&gt;&amp;#34;title=SSH Login Alert&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -F &lt;span style="color:#e6db74"&gt;&amp;#34;message=User \&amp;#34;&lt;/span&gt;$PAM_USER&lt;span style="color:#e6db74"&gt;\&amp;#34; logged in from \&amp;#34;&lt;/span&gt;$PAM_RHOST&lt;span style="color:#e6db74"&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make it executable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo chmod +x /usr/local/bin/pam-ssh-notify.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="step-3-update-pam-configuration"&gt;Step 3: Update PAM Configuration&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Edit the PAM configuration for SSH:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo vim /etc/pam.d/sshd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add this line at the &lt;strong&gt;end&lt;/strong&gt; of the file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;session optional pam_exec.so /usr/local/bin/pam-ssh-notify.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why optional?&lt;/strong&gt;
If Gotify is unreachable and this script fails, PAM will ignore the failure.
If you use &lt;code&gt;required&lt;/code&gt;, login failures could result from simple network errors — not ideal for a monitoring hook.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-4-enable-and-test-it"&gt;Step 4: Enable and Test It&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Ensure the &lt;code&gt;UsePAM&lt;/code&gt; setting is set to &amp;lsquo;yes&amp;rsquo; inside of &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; (or wherever your configuration is).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restart SSH:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl restart sshd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Log in from another machine and confirm that your Gotify app receives a notification.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="appendix-pam-environment-variables"&gt;Appendix: PAM Environment Variables&lt;/h2&gt;
&lt;p&gt;When PAM executes your custom script, it passes in useful environment variables that can be used to enhance logic, filtering, and alerting.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PAM_USER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User being authenticated&lt;/td&gt;
&lt;td&gt;Notify only on root logins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PAM_RHOST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote IP or hostname&lt;/td&gt;
&lt;td&gt;GeoIP filter or blacklist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PAM_SERVICE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service name (e.g. &lt;code&gt;sshd&lt;/code&gt;, &lt;code&gt;sudo&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Alert only on &lt;code&gt;sshd&lt;/code&gt; logins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PAM_TTY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terminal device (e.g. &lt;code&gt;pts/0&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Log which TTY was used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PAM_TYPE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Event type (&lt;code&gt;auth&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;td&gt;Differentiate login vs session start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PAM_RUSER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Originating user (e.g. for &lt;code&gt;sudo&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Log who used &lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="considerations"&gt;Considerations&lt;/h2&gt;
&lt;p&gt;While this PAM-to-Gotify setup is simple and effective, it&amp;rsquo;s best suited for single-node systems like personal VPS setups or small self-hosted boxes, systems you&amp;rsquo;re willing to give your full attention to.
It is &lt;strong&gt;not scalable&lt;/strong&gt; for large fleets or production environments with dozens or hundreds of machines.
For those cases, you&amp;rsquo;d want something more robust and asynchronous — like a logging agent with webhook triggers or a centralized security event bus.
For a midrange use case with an audit trail, I&amp;rsquo;ve also swapped out Gotify for a custom Slack App, using the channel webhooks in the past.&lt;/p&gt;
&lt;p&gt;Also, a critical caution: setting your PAM module to &lt;code&gt;required&lt;/code&gt; instead of &lt;code&gt;optional&lt;/code&gt; can be dangerous.
If Gotify (or DNS) is down, a &lt;code&gt;required&lt;/code&gt; script failure will prevent user logins entirely.
This is not hypothetical — in 2021 Facebook famously
&lt;a
href="https://en.wikipedia.org/wiki/2021_Facebook_outage"
target="_blank"
&gt;locked themselves out of their own data center&lt;/a
&gt; because DNS broke, and they couldn&amp;rsquo;t log in to fix the DNS server.
By marking this hook as &lt;code&gt;optional&lt;/code&gt;, you avoid cascading failures. Learn from their mistakes.&lt;/p&gt;
&lt;p&gt;PAM is capable of much more&amp;ndash;adding 2-factor authentication, YubiKey support, LDAP auth, encrypted filesystem mounting, etc.
If you want to learn more,
&lt;a
href="https://wiki.archlinux.org/title/PAM"
target="_blank"
&gt;the ArchLinux Wiki&lt;/a
&gt; is a great source, even if you&amp;rsquo;re not running Arch yourself.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With just a few lines of shell and PAM configuration, you can build powerful real-time login alerts and security automation using Gotify.
The &lt;code&gt;$PAM_*&lt;/code&gt; variables give you rich context for decision-making and logging, and the system is lightweight and reliable.&lt;/p&gt;
&lt;p&gt;Feel free to extend this setup to logins via &lt;code&gt;sudo&lt;/code&gt;, &lt;code&gt;su&lt;/code&gt;, or even failed login attempts — and adapt it to your environment as needed.&lt;/p&gt;
&lt;p&gt;Stay secure!&lt;/p&gt;</description></item><item><title>Temporal Server: Self-hosting a Production-Ready Instance</title><link>https://blog.taigrr.com/blog/setting-up-a-production-ready-temporal-server/</link><pubDate>Tue, 29 Apr 2025 18:19:25 +0600</pubDate><author>tai@taigrr.com (Tai Groot)</author><guid>https://blog.taigrr.com/blog/setting-up-a-production-ready-temporal-server/</guid><description>&lt;p&gt;
&lt;a
href="https://temporal.io/"
target="_blank"
&gt;Temporal&lt;/a
&gt; is an open-source workflow orchestration engine designed to manage, execute, and monitor complex workflows and activities.
In plain terms, Temporal allows you to write &lt;strong&gt;crash-proof&lt;/strong&gt; code: it ensures that your long-running processes (workflows) continue execution reliably even if your services (or external services!) crash or restart.&lt;/p&gt;
&lt;p&gt;Temporal&amp;rsquo;s
&lt;a
href="https://temporal.io/setup/start-development-server"
target="_blank"
&gt;walkthroughs&lt;/a
&gt; and CLI provide extremely easy adoption and low-friction for getting started: the temporal CLI contains an embedded development server (&lt;code&gt;temporal server start-dev&lt;/code&gt;), which binds to localhost and allows for easy, unauthenticated workflows.
Connecting to Temporal&amp;rsquo;s managed service is similarly easy, but at writing, has a minimum cost of
&lt;a
href="https://temporal.io/pricing"
target="_blank"
&gt;$100/month&lt;/a
&gt;, which is not always affordable for smaller orgs, especially when running a production-grade self-managed server can cost less than a quarter of that.&lt;/p&gt;
&lt;p&gt;In this article, we’ll set up a &lt;strong&gt;single-node Temporal server&lt;/strong&gt; backed by PostgreSQL, and we’ll secure it with TLS using NGINX as a reverse proxy.
The end result will be Temporal’s services running in Docker containers, accessible at &lt;strong&gt;&lt;code&gt;https://temporal.example.com&lt;/code&gt;&lt;/strong&gt; (our example domain) with encrypted connections.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This setup is suitable for small-scale, development and production deployments. Temporal is designed to scale horizontally and run multiple nodes for high availability and heavy workloads. For production, you should consider a multi-node cluster and follow Temporal’s production best practices (e.g. running each Temporal service separately, managing database schemas manually, etc.) ​
&lt;a
href="https://community.temporal.io/t/local-cluster-external-persistence/5891#:~:text=Here%20is%20the%20docker%20template,server%20role%20in%20own%20container"
target="_blank"
&gt;community.temporal.io&lt;/a
&gt;.
There is significant effort required to deploy and manage multiple horizontal Temporal servers, between certificate rotation and configuration management, especially as Temporal often changes configuration requirements. Once you get to the stage where you need the reliability of multiple Temporal servers, my recommendation is that you
&lt;a
href="https://temporal.io/get-cloud"
target="_blank"
&gt;pay for their managed service&lt;/a
&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="overview-of-the-deployment"&gt;Overview of the Deployment&lt;/h2&gt;
&lt;p&gt;Our deployment will consist of the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Temporal Server&lt;/strong&gt; – runs Temporal’s core services (History, Matching) in one container. We will use the official &lt;code&gt;temporalio/auto-setup&lt;/code&gt; image for convenience, which launches all services in one process and auto-initializes the database schema
&lt;a
href="https://docs.temporal.io/self-hosted-guide/deployment#:~:text=The%20%60temporalio%2Fdocker,for%20producing%20your%20own%20images"
target="_blank"
&gt;docs.temporal.io&lt;/a
&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Temporal Web UI&lt;/strong&gt; – runs in a separate container (&lt;code&gt;temporalio/ui&lt;/code&gt; image) to provide a web interface for viewing workflows, tasks, and Temporal namespaces&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt; – a single Postgres instance as Temporal’s persistence store (keeping workflow state, history, etc.). I recommend using a managed database for this, like
&lt;a
href="https://www.digitalocean.com/?refcode=aee820706adb&amp;amp;utm_campaign=Referral_Invite&amp;amp;utm_medium=Referral_Program&amp;amp;utm_source=badge"
target="_blank"
&gt;DigitalOcean&lt;/a
&gt; (Disclaimer: this is my personal RefLink to DigitalOcean) to ensure uptime and reliability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NGINX&lt;/strong&gt; – runs on the host as a reverse proxy. It will terminate TLS (HTTPS), forwarding requests to the Temporal WebUI container, and provide Basic Auth support.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these services can run on a single machine via Docker Compose (again, I do &lt;em&gt;recommend&lt;/em&gt; using a managed Postgres instance for stability).
The domain &lt;code&gt;temporal.example.com&lt;/code&gt; will point to this machine (use an A or CNAME record!), and we’ll obtain an SSL certificate for it using Let’s Encrypt.&lt;/p&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Before we begin, make sure you have the following on your VPS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker and Docker Compose&lt;/strong&gt;: This is how we&amp;rsquo;ll be standing up our temporal services and ensuring they automatically restart&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A domain name&lt;/strong&gt;: for accessing Temporal (e.g., &lt;code&gt;temporal.example.com&lt;/code&gt;), with DNS records pointing to your server’s IP. Don&amp;rsquo;t forget to reserve a public IP! Some cloud platforms will auto-release and reassign a new IP for you if you reboot, and this will break your DNS!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Firewall Rules&lt;/strong&gt;: Block all incoming traffic to your server, &lt;em&gt;especially&lt;/em&gt; port 8080, and only allow through &lt;code&gt;TCP/7233&lt;/code&gt;, &lt;code&gt;TCP/80&lt;/code&gt;, and &lt;code&gt;TCP/443&lt;/code&gt;. Later, you can whitelist your worker nodes for 7233, or just allow all (&lt;code&gt;0.0.0.0&lt;/code&gt;) to access this port.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Certbot&lt;/strong&gt;: Let&amp;rsquo;s Encrypt&amp;rsquo;s CertBot utility can be helpful for provisioning and rotating SSL certs in nginx&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GRPCurl&lt;/strong&gt;: A grpc CLI for doing quick spot-checks on the installation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;temporal (cli)&lt;/strong&gt;: A handy CLI utility for interacting with the Temporal server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re running an Ubuntu/Debian/Debian Derivative, this shell snippet may help:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt install docker-compose-v2 nginx python3-certbot-nginx apache2-utils golang
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -L &lt;span style="color:#e6db74"&gt;&amp;#39;https://temporal.download/cli/archive/latest?platform=linux&amp;amp;arch=amd64&amp;#39;&lt;/span&gt; &amp;gt; x.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mv temporal /usr/bin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rm LICENSE x.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, with that out of the way, let’s get started!&lt;/p&gt;
&lt;h3 id="setting-up-our-docker-compose"&gt;Setting Up Our Docker Compose&lt;/h3&gt;
&lt;p&gt;First, create a directory for your Temporal deployment and navigate into it.
Then create a file named &lt;code&gt;docker-compose.yml&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;3.5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;temporal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporalio/auto-setup:${TEMPORAL_VERSION:-1.24.2.1}&lt;/span&gt; &lt;span style="color:#75715e"&gt;# You can substitute for the latest Temporal version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env_file&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.env&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;restart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;temporal-network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;7233&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;7233&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Expose gRPC frontend port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./certs:/etc/temporal/certs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./base.yaml:/etc/temporal/config/base.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;temporal-ui&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporal-ui&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporalio/ui:${TEMPORAL_VERSION:-1.24.2.1}&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Use the latest Temporal Web UI version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env_file&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.env.ui&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;restart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;depends_on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;temporal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;temporal-network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Expose Web UI port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./ui.yaml:/etc/temporal/config/development.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./certs:/etc/temporal/certs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;temporal-network&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;driver&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bridge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporal-network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, let&amp;rsquo;s set up Postgres.&lt;/p&gt;
&lt;h3 id="configuring-postgres"&gt;Configuring Postgres&lt;/h3&gt;
&lt;p&gt;There are many ways to configure your Postgres instance, and plenty of ways to restrict permissions.
Here are the minimum requirements to make it work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a database named &lt;code&gt;temporal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create a database named &lt;code&gt;temporal_visibility&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create a user for the Temporal server to use (here we&amp;rsquo;ll assume &lt;code&gt;tuser&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;psql&lt;/code&gt; on your personal/work machine, you&amp;rsquo;ll need to connect to the Postgres instance as an admin and grant full access to the user on these two databases:&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Don&amp;rsquo;t forget to whitelist the IP address of your temporal server to access the database! Similarly, you may need to whitelist whichever machine you&amp;rsquo;re using to connect to the database with &lt;code&gt;psql&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CONNECT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DATABASE&lt;/span&gt; temporal &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;USAGE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SCHEMA&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SCHEMA&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ALTER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;PRIVILEGES&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IN&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SCHEMA&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ALL&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; TABLES &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Do the same for &lt;code&gt;temporal_visibility&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CONNECT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DATABASE&lt;/span&gt; temporal_visibility &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;USAGE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SCHEMA&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SCHEMA&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ALTER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;PRIVILEGES&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IN&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SCHEMA&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;GRANT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ALL&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; TABLES &lt;span style="color:#66d9ef"&gt;TO&lt;/span&gt; tuser;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, let&amp;rsquo;s create some TLS certificates.&lt;/p&gt;
&lt;h3 id="tls-certificates"&gt;TLS certificates&lt;/h3&gt;
&lt;p&gt;Make a directory called certs, and &lt;code&gt;cd&lt;/code&gt; into it.
Inside this directory, we&amp;rsquo;ll generate a RootCA file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl genrsa -out rootCA.key &lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl req -x509 -new -nodes -key rootCA.key -sha256 -days &lt;span style="color:#ae81ff"&gt;3650&lt;/span&gt; -out rootCA.pem
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can adjust the expiration time or keysize as you wish.&lt;/p&gt;
&lt;p&gt;Create a client and server key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl genrsa -out client.key &lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl genrsa -out server.key &lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, using our RootCA, we&amp;rsquo;ll mint some certificates.
Let&amp;rsquo;s start by creating some Certificicate Signing Request (&lt;code&gt;.csr&lt;/code&gt;) files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl req -new -key server.key -out server.csr -config server.cnf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll be prompted to answer some questions about your organization.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be sure the commonName and field matches your DNS record, i.e. &lt;code&gt;temporal.example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;For DNS Alt-names, provide the following:
&lt;ul&gt;
&lt;li&gt;DNS.1: temporal&lt;/li&gt;
&lt;li&gt;DNS.2: temporal.example.com&lt;/li&gt;
&lt;li&gt;IP.1: 127.0.0.1&lt;/li&gt;
&lt;li&gt;IP.2: &lt;code&gt;&amp;lt;YOUR RESERVED IP HERE&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And similarly, for the client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl req -new -key client.key -out client.csr -config client.cnf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, the values don&amp;rsquo;t really matter.
Configure as you see fit.&lt;/p&gt;
&lt;p&gt;Now that we have our &lt;code&gt;.csr&lt;/code&gt;s, let&amp;rsquo;s generate the certificates:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl x509 -req -in client.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out client.crt -days &lt;span style="color:#ae81ff"&gt;3650&lt;/span&gt; -sha256 -extfile client.cnf -extensions v3_req
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days &lt;span style="color:#ae81ff"&gt;3650&lt;/span&gt; -sha256 -extfile server.cnf -extensions v3_req
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, you should have a directory that looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;temporal@temporal-server:~$ tree
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── certs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── client.cnf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── client.crt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── client.csr
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── client.key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── rootCA.key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── rootCA.pem
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── rootCA.srl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── server.cnf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── server.crt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── server.csr
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   └── server.key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we need to create our &lt;code&gt;.env&lt;/code&gt; and &lt;code&gt;.env.ui&lt;/code&gt; files.&lt;/p&gt;
&lt;h3 id="configuring-env-vars"&gt;Configuring Env Vars&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file next to your &lt;code&gt;docker-compose.yml&lt;/code&gt;, replacing the &lt;code&gt;&amp;lt;SUBSTITUTIONS&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;POSTGRES_USER&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;YOUR USER HERE&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;POSTGRES_PWD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;PASSWORD HERE&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DB&lt;span style="color:#f92672"&gt;=&lt;/span&gt;postgres12_pgx
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DBNAME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;temporal
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VISIBILITY_DBNAME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;temporal_visibility
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DB_PORT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;YOUR DB PORT&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;POSTGRES_TLS_ENABLED&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;POSTGRES_SEEDS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;YOU DB HOSTNAME/IP HERE&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;POSTGRES_TLS_DISABLE_HOST_VERIFICATION&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SQL_TLS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SQL_TLS_DISABLE_HOST_VERIFICATION&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SQL_TLS_ENABLED&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SQL_HOST_VERIFICATION&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SKIP_DB_CREATE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_AUTH_ENABLED&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_ADDRESS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;temporal:7233
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_TLS_REQUIRE_CLIENT_AUTH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_TLS_CERTS_DIR&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/temporal/certs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SKIP_DEFAULT_NAMESPACE_CREATION&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DEFAULT_NAMESPACE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;default
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_CLI_TLS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_CLI_TLS_CERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/temporal/certs/client.crt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_CLI_TLS_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/temporal/certs/client.key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_CLI_TLS_CA_CERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/temporal/certs/rootCA.pem
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_ADDRESS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;temporal:7233
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, create a &lt;code&gt;.env.ui&lt;/code&gt; file to configure the webui:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_TLS_CA_DATA&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;REPLACE WITH BASE64 CERT rootCA.pem&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_TLS_CERT_DATA&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;REPLACE WITH BASE64 CERT client.crt&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEMPORAL_TLS_KEY_DATA&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;REPLACE WITH BASE64 CERT client.key&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can create these base64-encoded values as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo TEMPORAL_TLS_CA_DATA&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;cat certs/rootCA.pem | base64 -w0&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &amp;gt; .env.ui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo TEMPORAL_TLS_CERT_DATA&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;cat certs/client.crt | base64 -w0&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &amp;gt;&amp;gt; .env.ui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo TEMPORAL_TLS_KEY_DATA&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;cat certs/client.key | base64 -w0&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &amp;gt;&amp;gt; .env.ui
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="temporal-configuration"&gt;Temporal Configuration&lt;/h3&gt;
&lt;p&gt;Next to our &lt;code&gt;docker-compose.yml&lt;/code&gt;, you need to create two configuration files, one for &lt;code&gt;temporal-server&lt;/code&gt; and one for the &lt;code&gt;webui&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;base.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;global&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tls&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;internode&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;certFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/server.crt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;keyFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/server.key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requireClientAuth&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;clientCaFiles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/rootCA.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;client&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;serverName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rootCaFiles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/rootCA.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;frontend&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;certFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/server.crt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;keyFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/server.key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requireClientAuth&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;clientCaFiles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/rootCA.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rootCaFiles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/rootCA.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;client&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;serverName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rootCaFiles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/rootCA.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;history&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matching&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;worker&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;log&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;level&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;debug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And &lt;code&gt;ui.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;tls&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;caFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/rootCA.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;certFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/client.crt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;keyFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/etc/temporal/certs/client.key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;enableHostVerification&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;serverName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;temporal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="spinning-up"&gt;Spinning Up&lt;/h3&gt;
&lt;p&gt;Next, run &lt;code&gt;docker compose up&lt;/code&gt; and see your services come to life!&lt;/p&gt;
&lt;p&gt;You should see a whole mess of tables populate in your Postgres instance, as the auto-setup image does its thing.&lt;/p&gt;
&lt;p&gt;Once the logs start to slow down and your server starts into a retry loop, go ahead and kill the process with &lt;code&gt;Ctrl+c&lt;/code&gt;.
Wait for the containers to spin down, and we have one change to make to our &lt;code&gt;docker-compose.yml&lt;/code&gt;: swap out the word &lt;code&gt;autosetup&lt;/code&gt; for &lt;code&gt;server&lt;/code&gt; to change our specified container image.
&lt;code&gt;autosetup&lt;/code&gt; is meant to help you easily provision database tables and schema, but
&lt;a
href="https://temporal.io/blog/auto-setup"
target="_blank"
&gt;isn&amp;rsquo;t suitable for production deployments&lt;/a
&gt;, so once our tables are set up, we switch it to the production-ready &lt;code&gt;temporalio/server&lt;/code&gt; image.&lt;/p&gt;
&lt;h3 id="configuring-nginx-as-a-tls-reverse-proxy"&gt;Configuring NGINX as a TLS Reverse Proxy&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re so close to complete! We only have one step left: setting up our &lt;code&gt;nginx&lt;/code&gt; reverse-proxy with password auth.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a config you can drop in on top of &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; (optionally: instead, add a &lt;code&gt;sites&lt;/code&gt; file and link it to &lt;code&gt;sites-enabled&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;user&lt;/span&gt; &lt;span style="color:#e6db74"&gt;www-data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;worker_processes&lt;/span&gt; &lt;span style="color:#e6db74"&gt;auto&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;pid&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/run/nginx.pid&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;error_log&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/var/log/nginx/error.log&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;include&lt;/span&gt; /etc/nginx/modules-enabled/*.conf;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;events&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;worker_connections&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;768&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;http&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;sendfile&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tcp_nopush&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;types_hash_max_size&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;include&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/nginx/mime.types&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;default_type&lt;/span&gt; &lt;span style="color:#e6db74"&gt;application/octet-stream&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ssl_protocols&lt;/span&gt; &lt;span style="color:#e6db74"&gt;TLSv1&lt;/span&gt; &lt;span style="color:#e6db74"&gt;TLSv1.1&lt;/span&gt; &lt;span style="color:#e6db74"&gt;TLSv1.2&lt;/span&gt; &lt;span style="color:#e6db74"&gt;TLSv1.3&lt;/span&gt;; &lt;span style="color:#75715e"&gt;# Dropping SSLv3, ref: POODLE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;access_log&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/var/log/nginx/access.log&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;gzip&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;upstream&lt;/span&gt; &lt;span style="color:#e6db74"&gt;temporal&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt; 127.0.0.1:&lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;auth_basic&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Temporal&lt;/span&gt; &lt;span style="color:#e6db74"&gt;Admins&lt;/span&gt; &lt;span style="color:#e6db74"&gt;Only!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;auth_basic_user_file&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/nginx/.htpasswd&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server_name&lt;/span&gt; &lt;span style="color:#e6db74"&gt;temporal.example.com&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;location&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_pass&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http://temporal&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_http_version&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Ensuring it can use websockets
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;Upgrade&lt;/span&gt; $http_upgrade;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;Connection&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;upgrade&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Real-IP&lt;/span&gt; $remote_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Forwarded-For&lt;/span&gt; $proxy_add_x_forwarded_for;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_redirect&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http://&lt;/span&gt; $scheme://;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;Host&lt;/span&gt; $http_host;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;include&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/nginx/conf.d/*.conf&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;include&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/nginx/sites-enabled/*&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add the following to your &lt;code&gt;/etc/hosts&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;127.0.0.1 temporal
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Generate a &lt;code&gt;.htpasswd&lt;/code&gt; file to control user authentication (you will be prompted to enter a password):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;YOUR USERNAME&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Enable and reload &lt;code&gt;nginx&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;systemctl enable --now nginx &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; systemctl reload nginx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Setup Certbot (this is interactive and may prompt you for your email):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;certbot -d temporal.example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Note: don&amp;rsquo;t forget to add &lt;code&gt;certbot renew&lt;/code&gt; to a cron job! LetsEncrypt certs typically expire every 3 months, so check for renewals every 1-2 months.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="testing-the-setup"&gt;Testing the Setup&lt;/h3&gt;
&lt;p&gt;Moment of truth: open your browser and navigate to &lt;strong&gt;&lt;code&gt;https://temporal.example.com&lt;/code&gt;&lt;/strong&gt;.
You should be greeted with a Basic Auth prompt.
Enter your username and password you generated earlier.
Then, you should see Temporal’s Web UI homepage.&lt;/p&gt;
&lt;img
title="image title"
loading="lazy"
decoding="async"
class="img img-fluid img-center"
width="1280"
height="829"
src="https://blog.taigrr.com/images/post/temporal-default-no-workflows_hu_ad86dbabdb893879.png"
alt="Default temporal web ui view without any workflows"
onerror="this.onerror='null';this.src='\/images\/post\/temporal-default-no-workflows_hu_ad86dbabdb893879.png'" /&gt;
&lt;script&gt;
window.addEventListener("load", (e) =&gt; {
const lightbox = GLightbox();
});
&lt;/script&gt;
&lt;p&gt;If you see something similar to the screenshot above, congratulations! 🎉 You now have a single-node Temporal server running with TLS.
The connection is secure (check that browser lock icon), and you can start building or testing workflows.
The UI will show zero workflows initially (since none have been run); it’s expected.&lt;/p&gt;
&lt;p&gt;If you see errors regarding &amp;ldquo;no default namespace&amp;rdquo;, you can manually create one using the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;temporal operator namespace create --namespace default --tls-ca-path certs/rootCA.pem --tls-cert-path certs/client.crt --tls-key-path certs/client.key
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="troubleshooting"&gt;Troubleshooting&lt;/h3&gt;
&lt;h4 id="health-check"&gt;Health Check&lt;/h4&gt;
&lt;p&gt;To further verify that gRPC traffic is working through TLS, you can use &lt;code&gt;grpcurl&lt;/code&gt; to check the health probe:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~/go/bin/grpcurl --cacert certs/rootCA.pem --cert certs/client.crt --key certs/client.key -d &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;service&amp;#34;: &amp;#34;temporal.api.workflowservice.v1.WorkflowService&amp;#34;}&amp;#39;&lt;/span&gt; temporal:7233 grpc.health.v1.Health/Check
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you see the following, &lt;code&gt;temporal-server&lt;/code&gt; is properly configured:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;SERVING&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="creating-a-test-workflow"&gt;Creating a Test Workflow&lt;/h4&gt;
&lt;p&gt;To verify your task queues are set up, you can create a &lt;code&gt;HELLO WORLD&lt;/code&gt; workflow (note the nested quotes, which are necessary):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;temporal workflow start --address temporal:7233 --type HelloWorldWorkflow --task-queue&lt;span style="color:#f92672"&gt;=&lt;/span&gt;HELLO_WORLD_TASK_QUEUE --tls-ca-path certs/rootCA.pem --tls-cert-path certs/client.crt --tls-key-path certs/client.key --input &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;Hello, World!&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="cleaning-up-tests"&gt;Cleaning up Tests&lt;/h4&gt;
&lt;p&gt;If you&amp;rsquo;ve created too many test workflows, you can Terminate them from within the Web UI or run the following to delete all of them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; x in &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;temporal workflow list --address temporal:7233 --tls-ca-path certs/rootCA.pem --tls-cert-path certs/client.crt --tls-key-path certs/client.key | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{ print $2 }&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; temporal workflow delete --address temporal:7233 --tls-ca-path certs/rootCA.pem --tls-cert-path certs/client.crt --tls-key-path certs/client.key -w $x
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="connecting-a-client"&gt;Connecting a Client&lt;/h3&gt;
&lt;p&gt;Because we have self-signed certificates, we need some way to get the certificates into our client.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how I do it in go (it will be similar in other languages):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tclient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;crypto/tls&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;crypto/x509&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;encoding/base64&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;math/rand&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;os&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sync&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;github.com/taigrr/temporal-worker/vars&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;go.temporal.io/sdk/client&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;tClient&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientTex&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sync&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Mutex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;New&lt;/span&gt;() (&lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Client&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientTex&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Lock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;clientTex&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Unlock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tClient&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tClient&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// base64-encoded TLS certs created the same way we did for .env.ui&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientCertData&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Getenv&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;vars&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;TemporalTLSCertData&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientCertKeyData&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Getenv&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;vars&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;TemporalTLSKeyData&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;serverRootCAData&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Getenv&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;vars&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;TemporalTLSCAData&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// the FQDN:PORT i.e. temporal.example.com:7233&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hostPort&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Getenv&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;vars&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;TemporalHostPort&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Getenv&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;vars&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;TemporalNamespace&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Decode the base64 encoded keys and certs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientCert&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;base64&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;StdEncoding&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DecodeString&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;clientCertData&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unable to decode client cert data: %w&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientCertKey&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;base64&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;StdEncoding&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DecodeString&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;clientCertKeyData&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unable to decode client cert key data: %w&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;serverRootCA&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;base64&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;StdEncoding&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DecodeString&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;serverRootCAData&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unable to decode server root CA data: %w&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cert&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tls&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;X509KeyPair&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;clientCert&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;clientCertKey&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unable to load cert and key pair: %w&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create Certpool to add the server root CA to the client pool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;certPool&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;x509&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewCertPool&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;certPool&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;AppendCertsFromPEM&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;serverRootCA&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Ensure no two workers have the same ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;randString&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;%04x&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;rand&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Intn&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0x10000&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Embed the git commit of the currently running code into the worker ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;commitHash&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Getenv&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;vars&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;WorkerCommit&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; len(&lt;span style="color:#a6e22e"&gt;commitHash&lt;/span&gt;) &amp;gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;commitHash&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;commitHash&lt;/span&gt;[:&lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;workerID&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;%s-%s-%s&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Getenv&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;vars&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;WorkerID&lt;/span&gt;), &lt;span style="color:#a6e22e"&gt;commitHash&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;randString&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Add the cert to the tls certificates in the ConnectionOptions of the Client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientOptions&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Options&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;HostPort&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;hostPort&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Namespace&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;ConnectionOptions&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ConnectionOptions&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;TLS&lt;/span&gt;: &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;tls&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Config&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Certificates&lt;/span&gt;: []&lt;span style="color:#a6e22e"&gt;tls&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Certificate&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;cert&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;RootCAs&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;certPool&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Identity&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;workerID&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;temporalClient&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Dial&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;clientOptions&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Errorf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unable to connect to Temporal: %w&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;tClient&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;temporalClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tClient&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;workerID&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="wrap-up"&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;We’ve successfully deployed Temporal on a single node with Docker Compose and added TLS termination with NGINX.&lt;/p&gt;
&lt;p&gt;In summary, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Introduced Temporal and set up a &lt;strong&gt;PostgreSQL-backed Temporal server&lt;/strong&gt; using Docker Compose&lt;/li&gt;
&lt;li&gt;Used Temporal’s auto-setup image to provision our databases&lt;/li&gt;
&lt;li&gt;Switched to the Temporal production server&lt;/li&gt;
&lt;li&gt;Deployed the &lt;strong&gt;Temporal Web UI&lt;/strong&gt; and passed it through NGINX via a friendly HTTPS URL&lt;/li&gt;
&lt;li&gt;Configured &lt;strong&gt;NGINX&lt;/strong&gt; to handle TLS, forwarding web requests to the UI, thereby securing the traffic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This setup provides a starting point for working with Temporal’s powerful workflow capabilities.
You can now write workflows in your language of choice (Go, Java, Python, etc.), point the SDK client to &lt;code&gt;temporal.example.com:7233&lt;/code&gt;, and begin executing workflows on your server&amp;ndash;just make sure your self-signed certificates are imported (see my go example above)!&lt;/p&gt;
&lt;p&gt;Before we conclude, a few final notes and best practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Security:&lt;/strong&gt; Your NGINX config can also restrict access by IP. If you intend to expose the Temporal UI or gRPC port more broadly, consider additional security layers, i.e. cloud infra-level firewalls&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistence:&lt;/strong&gt; Your temporal workflows are only as stable as your database. Again, I highly recommend using a stable, managed solution&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Certificates&lt;/strong&gt;: A time will come when your certificates expire. Be sure to have a rotation plan in place for scheduled maintenance!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Postgres&lt;/strong&gt;: We initially granted sweeping access to the &lt;code&gt;tuser&lt;/code&gt; in Postgres for our &lt;code&gt;temporal&lt;/code&gt; and &lt;code&gt;temporal_visibility&lt;/code&gt; databases. Now that the schema is set up, consider locking that down.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Temporal brings reliability superpowers to your applications – even in this modest single-node setup, you can develop and test workflows that will &lt;strong&gt;never give up&lt;/strong&gt; in the face of failures.
Now that you have Temporal running at &lt;code&gt;temporal.example.com&lt;/code&gt;, it’s time to start orchestrating some workflows.
Have fun exploring Temporal, and may your workflows always complete (eventually)! 🚀&lt;/p&gt;
&lt;img
title="image title"
loading="lazy"
decoding="async"
class="img img-fluid img-center"
width="1220"
height="829"
src="https://blog.taigrr.com/images/post/temporal-many-workflows_hu_4bf27db1379907e4.png"
alt="Default temporal web ui view without any workflows"
onerror="this.onerror='null';this.src='\/images\/post\/temporal-many-workflows_hu_4bf27db1379907e4.png'" /&gt;</description></item><item><title>Setting up a WireGuard VPN for Remote Access</title><link>https://blog.taigrr.com/blog/provisioning-wireguard/</link><pubDate>Thu, 24 Mar 2022 18:19:25 +0600</pubDate><author>tai@taigrr.com (Tai Groot)</author><guid>https://blog.taigrr.com/blog/provisioning-wireguard/</guid><description>&lt;h3 id="what-is-wireguard"&gt;What is WireGuard?&lt;/h3&gt;
&lt;p&gt;WireGuard is a VPN, which tunnels traffic over encrypted, secure UDP packet streams.
Once a tunnel is established, the endpoints are allowed to roam (change IP addresses, such as might happen switching between WiFi networks or when connected to a cell tower).
It is special when compared to other VPN technologies for many technical reasons, which led to its rapid adoption into the Linux Kernel, but three features in particular make WireGuard stand out: the CryptoKey Routing Table, the way public/private keypairs are handled, and the &amp;ldquo;silent by default&amp;rdquo; behavior.&lt;/p&gt;
&lt;p&gt;According to the
&lt;a
href="https://www.wireguard.com/"
target="_blank"
&gt;wireguard website&lt;/a
&gt;,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography.
It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache.
It intends to be considerably more performant than OpenVPN.
WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances.
Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable.
It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="wireguard-basics"&gt;WireGuard Basics&lt;/h3&gt;
&lt;p&gt;Unlike traditional VPN technologies, WireGuard has no concept of Client or Server, but instead all nodes on the network are simply &lt;code&gt;Peer&lt;/code&gt;s, connecting Point-to-Point.
This allows for some really interesting scenarios where packets sent through the VPN can be sent through very specific routes by setting IP masks appropriately, and if all &lt;code&gt;Peer&lt;/code&gt;s on the VPN have public IP addresses (or else they can &amp;lsquo;see&amp;rsquo; each other in some other way, perhaps by sharing an insecure local network), the single point of failure built into the Hub-and-Spoke model becomes optional.&lt;/p&gt;
&lt;img
title="image title"
loading="lazy"
decoding="async"
class="img img-fluid img-center"
width="1000"
height="459"
src="https://blog.taigrr.com/images/post/hubAndSpoke_hu_229d0c30fce7fc04.png"
alt="Hub and Spoke vs. P2P"
onerror="this.onerror='null';this.src='\/images\/post\/hubAndSpoke_hu_229d0c30fce7fc04.png'" /&gt;
&lt;p&gt;However, even if WireGuard doesn&amp;rsquo;t specifically recognize the Hub and Spoke model, it&amp;rsquo;s still a valid configuration,and we&amp;rsquo;ll be using it for this tutorial.
Let&amp;rsquo;s see how easy it is to configure a Hub and Spoke-style, simple WireGuard VPN ready for personal or commercial use.&lt;/p&gt;
&lt;h4 id="server-instructions"&gt;Server Instructions&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Choose a private IP block to reserve.
In order to use WireGuard inside of a Spoke and Hub paradigm, it&amp;rsquo;s necessary to reserve a block of IP addresses for all the nodes.
(When running Peer to Peer, it&amp;rsquo;s not necessary for these addresses to share a subnet, as a CIDR of /32 can be used for each peer.)
Private IP ranges include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;10.0.0.0&lt;/code&gt; to &lt;code&gt;10.255.255.255&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;172.16.0.0&lt;/code&gt; to &lt;code&gt;172.31.255.255&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.0.0&lt;/code&gt; to &lt;code&gt;192.168.255.255&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An example range for WireGuard might look like: &lt;code&gt;192.168.2.1-192.168.2.255&lt;/code&gt; (be sure this range doesn&amp;rsquo;t conflict with that of your router).
In CIDR notation, this range would be: &lt;code&gt;192.168.2.0/24&lt;/code&gt;.
You can use a
&lt;a
href="https://www.subnet-calculator.com/cidr.php"
target="_blank"
&gt;CIDR Calculator&lt;/a
&gt; to determine the proper format for a given range.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose a port for your tunneled traffic to use.&lt;/strong&gt;
WireGuard traffic flows over UDP, and by default uses port &lt;code&gt;51820&lt;/code&gt;.
For this tutorial, we will assume you are using the default port, but if you are running multiple WireGuard VPNs on the same server, you will need to choose a different port for each one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose a cloud provider that provides you with a public IP address.&lt;/strong&gt;
My personal preference is
&lt;a
href="https://www.linode.com/?r=25e315c408f1c8da14e13d91a59e2e11e81ac639"
target="_blank"
&gt;Linode/Akamai&lt;/a
&gt; (by using my signup link you&amp;rsquo;ll get free credits, as will I.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you are ready to spin up a server, &lt;strong&gt;be sure to choose a distribution/flavor which ships Linux 5.6 or above.&lt;/strong&gt;
(Some versions of Ubuntu with older kernels also received backports, but if you have the option, go with the latest stable release available.)
It is possible to use WireGuard in Userspace (if you don&amp;rsquo;t know what this means, don&amp;rsquo;t worry about it, just use a newer installation image) but it is much slower than the version running in the kernel.
Going forward, we will assume you have the public IP of &lt;code&gt;93.184.216.34&lt;/code&gt; (used for
&lt;a
href="https://example.com"
target="_blank"
&gt;example.com&lt;/a
&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install WireGuard and its helper utilities.&lt;/strong&gt;
The name of the helper utilities package is usually &lt;code&gt;wireguard-tools&lt;/code&gt;, and installing that package should bring in &lt;code&gt;wireguard&lt;/code&gt; as well.
If you don&amp;rsquo;t already have &lt;code&gt;iptables&lt;/code&gt; installed, you will need to install that as well.
(On Debian-based systems, run &lt;code&gt;apt install wireguard-tools iptables&lt;/code&gt;.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enable IP forwarding.&lt;/strong&gt;
This is a necessary step for the server to act as a router.
On Linux, this is done by running &lt;code&gt;sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;.
To make this change permanent, edit or create &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; and add the line &lt;code&gt;net.ipv4.ip_forward=1&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a keypair for your server.&lt;/strong&gt;
Once WireGuard is installed, you can generate a private key by running &lt;code&gt;wg genkey&lt;/code&gt;.
To get the public key, run &lt;code&gt;wg pubkey&lt;/code&gt; and paste (or pipe) in the private key.
This keypair is just like an SSH public/private keypair, so keep it safe!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use the keypairs to create a configuration file.&lt;/strong&gt;
The next step is to create a configuration file for your server.
There are three main pieces of information required:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the interface and port to listen on&lt;/li&gt;
&lt;li&gt;the WireGuard private IP and netmask (CIDR Notation) you are committing to use (such as &lt;code&gt;192.168.2.1/24&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;the Private Key of your server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create a file at &lt;code&gt;/etc/wireguard/my-vpn.conf&lt;/code&gt; and add in the following:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[Interface]
ListenPort = 51820
PrivateKey = &amp;lt;SERVER PRIVATE KEY HERE&amp;gt; # &amp;lt;SERVER PUBLIC KEY HERE&amp;gt;
Address = 192.168.2.1/24
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Many systems use &lt;code&gt;eth0&lt;/code&gt; as the name of the primary network interface, but if yours is different, you will need to change it in the &lt;code&gt;PostUp&lt;/code&gt; and &lt;code&gt;PostDown&lt;/code&gt; commands.
If you are unsure of your default gateway&amp;rsquo;s interfacve name, run &lt;code&gt;ip a&lt;/code&gt; and look for the interface with the public IP address you are using to connect to the server.&lt;/p&gt;
&lt;p&gt;Additionally, you will add a block for each &lt;code&gt;Peer&lt;/code&gt;, but we&amp;rsquo;ll need to generate those keypairs first.
More on that later.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be sure the connection port is exposed through your hosting provider (for UDP traffic).&lt;/strong&gt;
Instructions for Linode can be found
&lt;a
href="https://www.linode.com/docs/products/networking/cloud-firewall/guides/"
target="_blank"
&gt;here&lt;/a
&gt;.
You may need to consult your provider&amp;rsquo;s documentation for more information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Before we start up our server, we need some more information from our clients.
We&amp;rsquo;ll come back to the server later.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="client-instructions"&gt;Client Instructions&lt;/h4&gt;
&lt;h5 id="linux-instructions"&gt;Linux instructions:&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Ensure your kernel has support for WireGuard, or install the userspace version.
On Ubuntu/Debian-based systems, run &lt;code&gt;apt install wireguard-tools&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create the folder &lt;code&gt;/etc/wireguard/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Generate a new keypair the same way you did on the server:
&lt;code&gt;wg genkey&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Place the keypair into a config file named &lt;code&gt;my-vpn.conf&lt;/code&gt; and save it, along with the details you got from the server.
The format is as follows:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[Interface]
PrivateKey = &amp;lt;CLIENT PRIVATE KEY HERE&amp;gt; # &amp;lt;CLIENT PUBLIC KEY HERE&amp;gt;
Address = 192.168.2.4/24 # Or whatever IP you want your client to use on the VPN
[Peer]
PublicKey = &amp;lt;SERVER PUBLIC KEY HERE&amp;gt;
AllowedIPs = 192.168.2.0/24 # This is the subnet you chose for your VPN, and should match the server&amp;#39;s
Endpoint = 93.184.216.34:51820 # This is the public IP of your server, and the port you chose
# Note you can also use a domain name, if you want:
# Endpoint = example.com:51820
PersistentKeepalive = 25 # allow your client to roam without dropping the connection
&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;chmod&lt;/code&gt; the config file and the &lt;code&gt;/etc/wireguard&lt;/code&gt; directory to &lt;code&gt;400&lt;/code&gt; and &lt;code&gt;chown&lt;/code&gt; it to &lt;code&gt;root:root&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On systemd-based systems, run &lt;code&gt;systemctl enable wg-quick@my-vpn&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Move on to the server section, when complete move on to the next step.&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id="mac-instructions"&gt;Mac instructions:&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Install
&lt;a
href="https://apps.apple.com/us/app/wireguard/id1451685025?mt=12"
target="_blank"
&gt;Wireguard for Mac&lt;/a
&gt;&lt;/li&gt;
&lt;li&gt;Follow prompts to fill in the info from your server.&lt;/li&gt;
&lt;li&gt;Choose an IP from the subnet you chose for your VPN.&lt;/li&gt;
&lt;li&gt;Save your Public Key for later.&lt;/li&gt;
&lt;li&gt;Enable start on demand.&lt;/li&gt;
&lt;li&gt;Complete the server steps next, before hitting &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="final-server-instructions"&gt;Final Server Instructions&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Inside of your &lt;code&gt;my-vpn.conf&lt;/code&gt; file, create a &lt;code&gt;Peer&lt;/code&gt; block following the pattern of the previous entries:
&lt;pre tabindex="0"&gt;&lt;code&gt;[Peer] # name of client
PublicKey = &amp;lt;CLIENT PUBLIC KEY HERE&amp;gt;
AllowedIPs = 192.168.2.&amp;lt;n&amp;gt;/32 # replace `&amp;lt;n&amp;gt;` with the last octet of the Client&amp;#39;s WireGuard private IP
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Save and close the config file.&lt;/li&gt;
&lt;li&gt;Ensure the config file is owned by &lt;code&gt;root&lt;/code&gt; and has permissions of &lt;code&gt;400&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;systemctl enable --now wg-quick@my-vpn&lt;/code&gt; to start the WireGuard tunnel and set it to start automatically on boot.&lt;/li&gt;
&lt;li&gt;Continue setting up from the client side.&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id="linux"&gt;Linux&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Same as for the server, run &lt;code&gt;systemctl enable --now wg-quick@my-vpn&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Validate the VPN is running with &lt;code&gt;wg&lt;/code&gt;.
You should see something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;interface: my-vpn
public key: &amp;lt;CLIENT PUBLIC KEY HERE&amp;gt;
private key: (hidden)
listening port: 58341
peer: &amp;lt;SERVER PUBLIC KEY HERE&amp;gt;
endpoint: 93.184.216.34:51820
allowed ips: 192.168.2.0/24
latest handshake: 13 seconds ago
transfer: 5.37 MiB received, 6.40 MiB sent
persistent keepalive: every 25 seconds
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you see an error, check the logs with &lt;code&gt;journalctl -u wg-quick@my-vpn&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id="macos"&gt;MacOS&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Hit &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Optionally, in your Mac&amp;rsquo;s System Preferences, go to &lt;code&gt;Network&lt;/code&gt; and check the box for &lt;code&gt;Enable VPN on Demand&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="next-steps"&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;You should now have a working WireGuard VPN, ready for roaming and personal use.
There&amp;rsquo;s only one client for now, but you can add as many as you like by repeating the client steps on each new device. (Be sure to pick a unique private IP address inside of the subnet you chose for your VPN for each client.)
Then, you can jump directly from one client to another using the WireGuard private IP addresses.
This can be particularly useful when pairing the
&lt;a
href="https://play.google.com/store/apps/details?id=com.wireguard.android&amp;amp;hl=en_US&amp;amp;gl=US"
target="_blank"
&gt;WireGuard app for Android&lt;/a
&gt; with
&lt;a
href="https://termux.com/"
target="_blank"
&gt;Termux&lt;/a
&gt; or
&lt;a
href="https://juicessh.com/"
target="_blank"
&gt;JuiceSSH&lt;/a
&gt; to access your laptop or desktop on the go.&lt;/p&gt;
&lt;p&gt;Once the client side setup is done, simply add a &lt;code&gt;Peer&lt;/code&gt; block for each client in the server&amp;rsquo;s config file, using a unique private IP address, then run &lt;code&gt;systemctl restart wg-quick@my-vpn&lt;/code&gt; on the server to restart the tunnel with the new configuration.
It is normal for the tunnel to drop when you run the restart command, but it should come back up within a few seconds.&lt;/p&gt;
&lt;p&gt;If you have any issues, check the logs with &lt;code&gt;journalctl -u wg-quick@my-vpn&lt;/code&gt;.
If you&amp;rsquo;re stumped, feel free to reach out to the community in my
&lt;a
href="https://discord.gg/FWvDANDvaP"
target="_blank"
&gt;Discord&lt;/a
&gt;, and someone may be able to help you out.&lt;/p&gt;
&lt;h3 id="going-further"&gt;Going Further&lt;/h3&gt;
&lt;p&gt;If you want to take your WireGuard VPN to the next level, you can add a DNS server to your server&amp;rsquo;s config file, and then add a &lt;code&gt;DNS&lt;/code&gt; entry to each client&amp;rsquo;s &lt;code&gt;Interface&lt;/code&gt; block.
You can read more about that
&lt;a
href="https://readtfm.org/title/WireGuard#DNS"
target="_blank"
&gt;here&lt;/a
&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to use WireGuard to route all of your traffic through the VPN, pair WireGuard with netns or Docker containers to force applications to use the VPN, or even use WireGuard to create a mesh network.
I&amp;rsquo;ll likely be writing more about these topics in the future.
For now, there are great resources available on the
&lt;a
href="https://www.wireguard.com/"
target="_blank"
&gt;WireGuard website&lt;/a
&gt; and on YouTube.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;&amp;ldquo;WireGuard&amp;rdquo; and the &amp;ldquo;WireGuard&amp;rdquo; logo are registered trademarks of Jason A. Donenfeld.&lt;/small&gt;&lt;/p&gt;</description></item></channel></rss>