<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community:  Carson Yang</title>
    <description>The latest articles on DEV Community by  Carson Yang (@carsonyang).</description>
    <link>https://dev.to/carsonyang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F766251%2F32d12cbb-a7ab-4873-863a-af5f40ddedaf.jpeg</url>
      <title>DEV Community:  Carson Yang</title>
      <link>https://dev.to/carsonyang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/carsonyang"/>
    <language>en</language>
    <item>
      <title>Headscale Deployment and Usage Guide: Mastering Tailscale's Self-Hosting Basics for Ultimate Control</title>
      <dc:creator> Carson Yang</dc:creator>
      <pubDate>Mon, 24 Nov 2025 04:18:40 +0000</pubDate>
      <link>https://dev.to/carsonyang/headscale-deployment-and-usage-guide-mastering-tailscales-self-hosting-basics-for-ultimate-5ai0</link>
      <guid>https://dev.to/carsonyang/headscale-deployment-and-usage-guide-mastering-tailscales-self-hosting-basics-for-ultimate-5ai0</guid>
      <description>&lt;p&gt;Headscale is an open-source server that works like Tailscale's control server. You can run it yourself instead of using Tailscale's hosted service. This gives you full control over your VPN network without device limits or subscription fees. Here's how to set it up and connect your devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Tailscale?
&lt;/h2&gt;

&lt;p&gt;Tailscale is a VPN built on &lt;strong&gt;WireGuard&lt;/strong&gt;. It works like other mesh VPN tools such as Netmaker. &lt;strong&gt;Tailscale runs WireGuard in user space, while Netmaker uses kernel-space WireGuard&lt;/strong&gt;. This means Tailscale has slightly lower performance than kernel-space solutions. But it's still much faster than OpenVPN and easier to use.&lt;/p&gt;

&lt;p&gt;Here's what makes Tailscale useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Simple setup:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  No firewall configuration needed.&lt;/li&gt;
&lt;li&gt;  Easy network setup.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Security:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  Automatic key rotation.&lt;/li&gt;
&lt;li&gt;  End-to-end encryption by default.&lt;/li&gt;
&lt;li&gt;  Access logs you can review.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;NAT traversal:&lt;/strong&gt; Uses &lt;strong&gt;DERP (Detoured Encrypted Routing Protocol)&lt;/strong&gt; over TCP when UDP doesn't work. This helps connections work even behind difficult firewalls.&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Central control:&lt;/strong&gt; Push access rules and settings to all devices from one place.&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Easy authentication:&lt;/strong&gt; Works with Google, Microsoft, GitHub, and other login providers.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Tailscale is basically WireGuard made easier to use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-03-20-14-50-Q4bWmK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-03-20-14-50-Q4bWmK.png" title="Tailscale Admin Console" alt="Tailscale Admin Console" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tailscale has a free tier that works well for personal use or small teams. You can use it free as long as you stay under the device limit (around 20 devices per account - check their website for current limits). The free tier has some limits on advanced features like subnet routing, but it covers most basic needs. Most Tailscale client code is open source under the BSD license (except Windows and macOS GUI apps). You can find the source code in their &lt;a href="https://github.com/tailscale/" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The free tier works for most people. If you need more features, they have paid plans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But what if you want full control, unlimited devices, and no subscription fees? That's where Headscale comes in.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Headscale?
&lt;/h2&gt;

&lt;p&gt;Tailscale's control server is proprietary and has limits for free users. That's fair since it's how they make money. But the open-source community built an alternative: &lt;a href="https://github.com/juanfont/headscale" rel="noopener noreferrer"&gt;Headscale&lt;/a&gt;. It's become the main open-source option for self-hosted Tailscale setups.&lt;/p&gt;

&lt;p&gt;Juan Font at the European Space Agency created Headscale. It's written in Go and released under the BSD license. Headscale copies most of Tailscale's control server features. You can run the whole coordination server yourself. This gives you full control over your network traffic and removes device limits. If you want to run your own secure VPN without restrictions, Headscale is a good choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Deploy Headscale
&lt;/h2&gt;

&lt;p&gt;Here are different ways to set up Headscale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Deploy with Sealos
&lt;/h3&gt;

&lt;p&gt;If you want to get started fast, the &lt;a href="https://sealos.io/products/app-store" rel="noopener noreferrer"&gt;Sealos App Store&lt;/a&gt; has one-click Headscale deployment. This is simple and needs almost no setup.&lt;/p&gt;

&lt;p&gt;Click one of these to deploy Headscale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SQLite version:&lt;/strong&gt;
&lt;a href="https://sealos.io/products/app-store/headscale" rel="noopener noreferrer"&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsealos.io%2FDeploy-on-Sealos.svg" alt="Deploy Headscale SQLite version on Sealos" width="185" height="42"&gt;
  &lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PostgreSQL version:&lt;/strong&gt;
&lt;a href="https://sealos.io/products/app-store/headscale-pg" rel="noopener noreferrer"&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsealos.io%2FDeploy-on-Sealos.svg" alt="Deploy Headscale PostgreSQL version on Sealos" width="185" height="42"&gt;
  &lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click "Deploy on Sealos" to start. Sign in to your Sealos account, then click "Deploy Application." When it's done, click on the Headscale app's "Details" to see how to access it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-11-24-12-02-AfqK0C.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-11-24-12-02-AfqK0C.png" title="Headscale application in Sealos App Store" alt="Headscale application listed in Sealos App Store" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your Headscale service will have a public domain that maps to internal port 8080.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-11-24-12-04-AN9dcH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-11-24-12-04-AN9dcH.png" title="Headscale app details in Sealos" alt="Headscale application details in Sealos" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the public address to access your Headscale dashboard. This usually opens Headplane, a web interface for managing Headscale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-00-DTBvOB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-00-DTBvOB.png" title="Headplane login page" alt="Headplane login page" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy on a Linux Server
&lt;/h3&gt;

&lt;p&gt;Setting up Headscale manually on Linux is pretty simple.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Headscale only needs internet access to work. But for best NAT traversal performance, use a cloud server with a public IP address. This avoids extra NAT layers and gives your Tailscale clients better connectivity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, download the latest Headscale binary from the &lt;a href="https://github.com/juanfont/headscale/releases" rel="noopener noreferrer"&gt;GitHub releases page&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Replace &amp;lt;HEADSCALE_VERSION&amp;gt; and &amp;lt;ARCH&amp;gt; with your version and architecture (e.g., v0.22.3, amd64)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;wget &lt;span class="nt"&gt;--output-document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/headscale &lt;span class="se"&gt;\&lt;/span&gt;
   https://github.com/juanfont/headscale/releases/download/v&amp;lt;HEADSCALE_VERSION&amp;gt;/headscale_&amp;lt;HEADSCALE_VERSION&amp;gt;_linux_&amp;lt;ARCH&amp;gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/headscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the config directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/headscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a directory for data storage (SQLite DB and SSL certificates):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/lib/headscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an empty SQLite database file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /var/lib/headscale/db.sqlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the example config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wget https://github.com/juanfont/headscale/raw/main/config-example.yaml &lt;span class="nt"&gt;-O&lt;/span&gt; /etc/headscale/config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now edit &lt;code&gt;/etc/headscale/config.yaml&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Set &lt;code&gt;server_url&lt;/code&gt; to your public IP or domain (e.g., &lt;code&gt;http://&amp;lt;YOUR_PUBLIC_IP_OR_DOMAIN&amp;gt;:8080&lt;/code&gt; or &lt;code&gt;https://&amp;lt;YOUR_PUBLIC_IP_OR_DOMAIN&amp;gt;&lt;/code&gt;).

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Note:&lt;/strong&gt; If using a domain in mainland China, make sure it has ICP filing. If not, just use your public IP address.&lt;/li&gt;
&lt;li&gt;  For HTTPS, make sure your TLS certificates are set up correctly.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  If you don't want MagicDNS initially, set &lt;code&gt;dns_config.magic_dns&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;  Enable randomized client ports for better NAT traversal by setting &lt;code&gt;randomize_client_port&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;You can customize your private network IP ranges:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ip_prefixes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# - fd7a:115c:a1e0::/48  # IPv6 prefix for your network&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;100.64.0.0/10&lt;/span&gt;        &lt;span class="c1"&gt;# Default address space used by Tailscale/Headscale&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Create a SystemD service file so Headscale runs automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/headscale.service&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;Unit]
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Headscale Controller - Self-hosted Tailscale coordination server
&lt;span class="nv"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;syslog.target
&lt;span class="nv"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;network.target

&lt;span class="o"&gt;[&lt;/span&gt;Service]
&lt;span class="nv"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;simple
&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;headscale
&lt;span class="nv"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;headscale
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/headscale serve
&lt;span class="nv"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always
&lt;span class="nv"&gt;RestartSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5

&lt;span class="c"&gt;# Security settings&lt;/span&gt;
&lt;span class="nv"&gt;NoNewPrivileges&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes
&lt;/span&gt;&lt;span class="nv"&gt;PrivateTmp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes
&lt;/span&gt;&lt;span class="nv"&gt;ProtectSystem&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;strict
&lt;span class="nv"&gt;ProtectHome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes
&lt;/span&gt;&lt;span class="nv"&gt;ReadWritePaths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/headscale /var/run/headscale
&lt;span class="nv"&gt;AmbientCapabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CAP_NET_BIND_SERVICE

&lt;span class="nv"&gt;RuntimeDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;headscale

&lt;span class="o"&gt;[&lt;/span&gt;Install]
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;code&gt;headscale&lt;/code&gt; user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;useradd headscale &lt;span class="nt"&gt;-d&lt;/span&gt; /home/headscale &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change ownership of the data directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; headscale:headscale /var/lib/headscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the &lt;code&gt;unix_socket&lt;/code&gt; path in &lt;code&gt;/etc/headscale/config.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;unix_socket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/headscale/headscale.sock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload SystemD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start Headscale and enable it to start at boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; headscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the service status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl status headscale
&lt;span class="c"&gt;# Look for "active (running)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check what ports Headscale is listening on (usually 8080 for HTTP, 9090 for gRPC):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ss &lt;span class="nt"&gt;-tulnp&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;headscale
&lt;span class="c"&gt;# Example output:&lt;/span&gt;
&lt;span class="c"&gt;# tcp LISTEN 0 1024 *:8080 *:* users:(("headscale",pid=...,fd=...))&lt;/span&gt;
&lt;span class="c"&gt;# tcp LISTEN 0 1024 *:9090 *:* users:(("headscale",pid=...,fd=...))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing Users in Headscale
&lt;/h2&gt;

&lt;p&gt;Tailscale uses "tailnets" - separate network groups that don't talk to each other. You can read more about this in Tailscale's docs: &lt;a href="https://tailscale.com/kb/1136/tailnet/" rel="noopener noreferrer"&gt;What is a tailnet?&lt;/a&gt;. In Headscale, these are called "users." You need to create at least one user before connecting devices to your network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Users via Command Line
&lt;/h3&gt;

&lt;p&gt;To create a user named &lt;code&gt;default&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;headscale &lt;span class="nb"&gt;users &lt;/span&gt;create default
&lt;span class="c"&gt;# Expected output: "User created"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see your users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;headscale &lt;span class="nb"&gt;users &lt;/span&gt;list
&lt;span class="c"&gt;# Example output:&lt;/span&gt;
&lt;span class="c"&gt;# ID | Name | Username | Email | Created&lt;/span&gt;
&lt;span class="c"&gt;# 1  |      | default  |       | 2025-05-26 05:03:48&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you used &lt;a href="https://sealos.run" rel="noopener noreferrer"&gt;Sealos&lt;/a&gt; to deploy Headscale, click the "Terminal" button on the Headscale app details page to open a terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-11-24-12-08-9yyeII.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-11-24-12-08-9yyeII.png" title="Sealos container terminal" alt="Sealos container terminal" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then run the commands above to create your user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Users with Headplane
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/tale/headplane" rel="noopener noreferrer"&gt;Headplane&lt;/a&gt; is a web interface for managing Headscale. It needs an API key to connect to your Headscale server. First, generate an API key.&lt;/p&gt;

&lt;p&gt;Run this command on your Headscale server (or in the Sealos terminal):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;headscale apikeys create
&lt;span class="c"&gt;# Copy the generated API key. Keep it safe like a password.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the API key in Headplane's settings page. After connecting, go to "Users" at the top, then click "Add a new user":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-10-NXTTgh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-10-NXTTgh.png" title="Headplane user creation interface" alt="Headplane user creation interface" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect Tailscale Clients to Headscale
&lt;/h2&gt;

&lt;p&gt;Tailscale clients on all major platforms can connect to custom control servers like Headscale. Older iOS clients worked a bit differently, but modern versions are simpler.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OS&lt;/th&gt;
&lt;th&gt;Works with Headscale&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CLI and GUI clients. Good for relay nodes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenBSD&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CLI client.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FreeBSD&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CLI client.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;GUI and CLI. See &lt;a href="https://github.com/juanfont/headscale/blob/main/docs/usage/connect/apple.md#macos" rel="noopener noreferrer"&gt;macOS docs&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;GUI interface. See &lt;a href="https://github.com/juanfont/headscale/blob/main/docs/usage/connect/windows.md" rel="noopener noreferrer"&gt;Windows docs&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;GUI interface. Server can be changed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;GUI interface. See &lt;a href="https://github.com/juanfont/headscale/blob/main/docs/usage/connect/apple.md#ios" rel="noopener noreferrer"&gt;iOS docs&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's how to set up Tailscale clients on different platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect Linux Clients
&lt;/h3&gt;

&lt;p&gt;Tailscale has official packages for Linux distributions. But users in some regions (like China) might get slow downloads from official repos. Tailscale also offers &lt;a href="https://tailscale.com/download/linux/static" rel="noopener noreferrer"&gt;static binaries&lt;/a&gt; you can download directly.&lt;/p&gt;

&lt;p&gt;Download the static version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Replace 1.XX.Y with the latest version and amd64 with your architecture&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;wget https://pkgs.tailscale.com/stable/tailscale_1.XX.Y_amd64.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf tailscale_1.XX.Y_amd64.tgz
&lt;span class="c"&gt;# Creates a directory like tailscale_1.XX.Y_amd64/ with the binaries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the binaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;tailscale_1.XX.Y_amd64/tailscaled /usr/sbin/tailscaled
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;tailscale_1.XX.Y_amd64/tailscale /usr/bin/tailscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the systemd service files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;tailscale_1.XX.Y_amd64/systemd/tailscaled.service /lib/systemd/system/tailscaled.service
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;tailscale_1.XX.Y_amd64/systemd/tailscaled.defaults /etc/default/tailscaled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; tailscaled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that it's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status tailscaled
&lt;span class="c"&gt;# Look for "active (running)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now connect your Linux client to Headscale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Replace &amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt; with your server's public IP or domain.&lt;/span&gt;
&lt;span class="c"&gt;# Use the right protocol (http/https) and port from your config.yaml.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale up &lt;span class="nt"&gt;--login-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://&amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt;:8080 &lt;span class="nt"&gt;--accept-routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--accept-dns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="c"&gt;# If you used Sealos deployment, HTTPS is usually set up already.&lt;/span&gt;
&lt;span class="c"&gt;# Replace &amp;lt;SEALOS_HEADSCALE_DOMAIN&amp;gt; with your Sealos domain.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale up &lt;span class="nt"&gt;--login-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://&amp;lt;SEALOS_HEADSCALE_DOMAIN&amp;gt; &lt;span class="nt"&gt;--accept-routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--accept-dns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also get connection commands from Headplane's "Machines" page, which fills in the server URL for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-18-27-CcSwYj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-18-27-CcSwYj.png" title="Connection commands from Headplane" alt="Tailscale connection commands from Headplane" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;--accept-dns=false&lt;/code&gt; at first to stop Headscale's MagicDNS from changing your system DNS settings. Only enable it if you need it and have set it up properly.&lt;/p&gt;

&lt;p&gt;After running &lt;code&gt;tailscale up&lt;/code&gt;, you'll see a registration link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;To authenticate, visit:

    https://headscale-your-instance.example.com/register/nodekey_xxxxxxxxxxxxxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-31-83GSjk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-31-83GSjk.png" title="Headscale registration page" alt="Headscale registration page" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open this link in your browser. The page will show you a command to run on your Headscale server to register the device. Or you can use Headplane: copy the node key (the long string after &lt;code&gt;/register/&lt;/code&gt;), go to "Machines" in Headplane, click "Add Device," paste the key in the &lt;code&gt;Machine Key&lt;/code&gt; field, pick an Owner (the Headscale user), and click "Confirm."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-30-NdgUF8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-30-NdgUF8.png" title="Adding a device in Headplane" alt="Adding a device in Headplane" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After registration, you'll see the device info in Headplane:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-33-WFP5Zh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-13-33-WFP5Zh.png" title="Registered device in Headplane" alt="Registered device in Headplane" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back on your Linux machine, Tailscale sets up the network automatically. This includes a network interface (like &lt;code&gt;tailscale0&lt;/code&gt;), routing tables, and iptables rules. To check the Tailscale routing table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ip route show table 52
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check iptables rules for Tailscale (if you use iptables):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-S&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;tailscale
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; nat | &lt;span class="nb"&gt;grep &lt;/span&gt;tailscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set Up macOS Clients
&lt;/h3&gt;

&lt;p&gt;There are three ways to install Tailscale on macOS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;App Store:&lt;/strong&gt; Get the app from the &lt;a href="https://apps.apple.com/app/tailscale/id1475387142" rel="noopener noreferrer"&gt;Mac App Store&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Direct download:&lt;/strong&gt; Download the &lt;code&gt;.pkg&lt;/code&gt; installer or &lt;code&gt;.zip&lt;/code&gt; file from Tailscale's website.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Command line:&lt;/strong&gt; Install the CLI tools. See the &lt;a href="https://github.com/tailscale/tailscale/wiki/Tailscaled-on-macOS" rel="noopener noreferrer"&gt;macOS documentation&lt;/a&gt; for details.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All three use the same networking code. They just differ in packaging and whether they have a GUI.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;App Store&lt;/th&gt;
&lt;th&gt;Standalone App&lt;/th&gt;
&lt;th&gt;Command Line&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GUI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Background Running&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automatic Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open Source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;File Transfer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;{{&amp;lt; alert &amp;gt;}}&lt;br&gt;
&lt;strong&gt;Important:&lt;/strong&gt; Pick either the App Store version or the standalone version. Don't install both.&lt;br&gt;
{{&amp;lt; /alert &amp;gt;}}&lt;/p&gt;

&lt;p&gt;After installing the Tailscale app, you need to configure it to use your Headscale server. Your Headscale server has setup instructions at &lt;code&gt;https://&amp;lt;YOUR_HEADSCALE_PUBLIC_ENDPOINT&amp;gt;/apple&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For Tailscale versions 1.34.0 and later:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Hold &lt;strong&gt;Option (⌥)&lt;/strong&gt; and click the Tailscale icon in your menu bar.&lt;/li&gt;
&lt;li&gt; Hover over the &lt;strong&gt;Debug&lt;/strong&gt; menu.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2023-12-29-17-04-fPLtsa.png" title="macOS Tailscale Debug menu" alt="macOS Tailscale Debug menu" width="800" height="438"&gt;
&lt;/li&gt;
&lt;li&gt; Select "Custom Login Server..." then "Add Account..." or "Change server...".&lt;/li&gt;
&lt;li&gt; Enter your Headscale URL (e.g., &lt;code&gt;https://&amp;lt;YOUR_HEADSCALE_DOMAIN&amp;gt;&lt;/code&gt;). &lt;strong&gt;Make sure to include &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;/strong&gt; Click "Add Account" or "Save."
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2023-12-29-17-13-0LVi0S.png" title="macOS Tailscale server configuration" alt="macOS Tailscale server configuration" width="520" height="482"&gt;
&lt;/li&gt;
&lt;li&gt; Tailscale will connect to your Headscale server and open a registration page in your browser.&lt;/li&gt;
&lt;li&gt; Register the device the same way as Linux: copy the node key, go to "Machines" in Headplane, click "Add Device," paste the key, pick an Owner, and click "Confirm."
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-14-15-RlBbWM.png" title="macOS registration success" alt="Headscale macOS registration success" width="800" height="502"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test connectivity by pinging another device in your network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 100.64.0.1 &lt;span class="c"&gt;# Ping another device's Tailscale IP&lt;/span&gt;
&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="c"&gt;# PING 100.64.0.1 (100.64.0.1): 56 data bytes&lt;/span&gt;
&lt;span class="c"&gt;# 64 bytes from 100.64.0.1: icmp_seq=0 ttl=64 time=37.025 ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use the Tailscale CLI if available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;/Applications/Tailscale.app/Contents/MacOS/Tailscale ping 100.64.0.1
&lt;span class="c"&gt;# Expected: pong from device-name (100.64.0.1) via ... in Xms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For older Tailscale versions (before 1.32.0), the setup might be different. Check your Headscale server's &lt;code&gt;/apple&lt;/code&gt; page for specific instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect Android Clients
&lt;/h3&gt;

&lt;p&gt;Android Tailscale has supported custom servers like Headscale since version 1.30.0. Download it from &lt;a href="https://play.google.com/store/apps/details?id=com.tailscale.ipn" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt; or &lt;a href="https://f-droid.org/packages/com.tailscale.ipn" rel="noopener noreferrer"&gt;F-Droid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open the Tailscale app. You'll see the login screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-12-m2IYpv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-12-m2IYpv.jpeg" title="Android Tailscale login screen" alt="Android Tailscale login screen" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tap the three-dot menu (⋮) in the top right. You'll only see an &lt;code&gt;About&lt;/code&gt; option at first:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-14-ghdl4A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-14-ghdl4A.jpeg" title="Android Tailscale menu" alt="Android Tailscale menu" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a trick to unlock the custom server option: quickly &lt;strong&gt;tap to open and close&lt;/strong&gt; the three-dot menu about 5-7 times. A &lt;code&gt;Change server&lt;/code&gt; option will appear:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-23-mcAexh.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-23-mcAexh.jpeg" title="Change server option unlocked" alt="Android Tailscale with Change server option" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tap &lt;code&gt;Change server&lt;/code&gt; and enter your Headscale URL (e.g., &lt;code&gt;https://&amp;lt;YOUR_HEADSCALE_DOMAIN&amp;gt;&lt;/code&gt;). &lt;strong&gt;Include &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-37-fkRIxW.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-11-22-18-37-fkRIxW.jpeg" title="Server address input" alt="Android server address input" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tap &lt;code&gt;Save and restart&lt;/code&gt;. After restart, tap &lt;code&gt;Sign in with other...&lt;/code&gt;. You'll go to your browser for registration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2024-01-07-19-39-fKOCTT.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2024-01-07-19-39-fKOCTT.jpg" title="Headscale authentication page" alt="Android Headscale authentication" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the machine key from the page. Register it on your Headscale server or via Headplane like before. After registration, go back to the app - it should show as connected:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2024-01-07-20-21-2e8CKX.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2024-01-07-20-21-2e8CKX.jpg" title="Android Tailscale connected" alt="Android Tailscale connected" width="800" height="1777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect Windows Clients
&lt;/h3&gt;

&lt;p&gt;To connect Windows Tailscale to Headscale, follow the instructions on your Headscale server's Windows page. Open &lt;code&gt;https://&amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt;/windows&lt;/code&gt; in your browser. This page shows you how to set it up, usually by editing the Windows Registry or using command-line parameters:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2023-12-29-17-19-vSqFK9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2023-12-29-17-19-vSqFK9.png" title="Windows setup instructions" alt="Headscale Windows setup instructions" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the instructions carefully. You'll usually edit registry entries to set the &lt;code&gt;ControlURL&lt;/code&gt; or use parameters like &lt;code&gt;/custom-server=&amp;lt;YOUR_HEADSCALE_URL&amp;gt;&lt;/code&gt; when installing or running Tailscale. After making changes, start the Tailscale client. It should open your browser for Headscale authentication, just like Linux and macOS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect Other Linux Devices
&lt;/h3&gt;

&lt;p&gt;Many people run Linux on routers (OpenWrt) or NAS systems (QNAP, Synology). The community has made tools for installing Tailscale and connecting to Headscale on these devices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;OpenWrt:&lt;/strong&gt; Check out &lt;a href="https://github.com/adyanth/openwrt-tailscale-enabler" rel="noopener noreferrer"&gt;adyanth/openwrt-tailscale-enabler&lt;/a&gt; for scripts and packages.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Synology NAS:&lt;/strong&gt; Tailscale has official packages. See &lt;a href="https://github.com/tailscale/tailscale-synology" rel="noopener noreferrer"&gt;tailscale/tailscale-synology&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;QNAP NAS:&lt;/strong&gt; Tailscale has official QNAP packages: &lt;a href="https://github.com/tailscale/tailscale-qpkg" rel="noopener noreferrer"&gt;tailscale/tailscale-qpkg&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the documentation in these repositories or your device's forums for setup instructions. These devices work great as subnet routers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect iOS Clients
&lt;/h3&gt;

&lt;p&gt;To use Tailscale on iPhone/iPad with Headscale, download the official app from the App Store. You might need an Apple ID from a region where Tailscale is available.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Download the &lt;a href="https://apps.apple.com/app/tailscale/id1475387142" rel="noopener noreferrer"&gt;Tailscale iOS app&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt; Open the app.&lt;/li&gt;
&lt;li&gt; Tap the account icon in the top-right, then "Log in...".
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-22-rkTD7z.png" title="iOS account screen" alt="iOS Tailscale account screen" width="800" height="1731"&gt;
&lt;/li&gt;
&lt;li&gt; On the login screen, tap the options menu (three dots or gear) and choose "Use custom coordination server".
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-24-6pQeUX.png" title="Custom server option" alt="iOS custom server option" width="800" height="1731"&gt;
&lt;/li&gt;
&lt;li&gt; Enter your Headscale URL (e.g., &lt;code&gt;https://&amp;lt;YOUR_HEADSCALE_DOMAIN&amp;gt;&lt;/code&gt;) and tap "Log in". You'll go to your Headscale authentication page.&lt;/li&gt;
&lt;li&gt; Register the device like before:

&lt;ul&gt;
&lt;li&gt;  Copy the machine key from the browser.&lt;/li&gt;
&lt;li&gt;  Go to "Machines" in Headplane, click "Add Device," paste the key, pick an Owner, and click "Confirm."&lt;/li&gt;
&lt;li&gt;  The iOS app will show the connection status and IP address.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-30-GRkcPG.png" title="iOS Tailscale connected" alt="iOS Tailscale connected" width="800" height="1731"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Auto-Register Devices with Pre-Auth Keys
&lt;/h3&gt;

&lt;p&gt;Headscale has "Pre-Authkeys" that let devices join automatically without manual approval each time. This is useful when adding many devices or automating setups.&lt;/p&gt;

&lt;p&gt;Generate a pre-auth key for your user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a key for 'default' user that expires in 24 hours&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;headscale preauthkeys create &lt;span class="nt"&gt;--user&lt;/span&gt; default &lt;span class="nt"&gt;--expiration&lt;/span&gt; 24h
&lt;span class="c"&gt;# Output includes: Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List your pre-auth keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;headscale preauthkeys list &lt;span class="nt"&gt;--user&lt;/span&gt; default
&lt;span class="c"&gt;# Shows: ID, Key, Reusable, Ephemeral, Used, Expiration, Created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also create keys in Headplane. Go to "Pre-auth keys" and click "Add a new Pre-auth key":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-33-PLkUmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-33-PLkUmf.png" title="Creating pre-auth key" alt="Headplane pre-auth key creation" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the user, expiration time, and whether it's reusable (multiple devices can use it) or single-use. Click "Confirm":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-34-9l5p0S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-34-9l5p0S.png" title="Pre-auth key created" alt="New pre-auth key created" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;View and manage your keys:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-35-tcCORl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-35-tcCORl.png" title="Pre-auth keys list" alt="Pre-auth keys list" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now devices can join automatically using the &lt;code&gt;--authkey&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Replace $AUTH_KEY with your actual key&lt;/span&gt;
&lt;span class="c"&gt;# Replace &amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt; with your server address&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale up &lt;span class="nt"&gt;--login-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://&amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt;:8080 &lt;span class="nt"&gt;--accept-routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--accept-dns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--authkey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$AUTH_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The device will register automatically under the user associated with the key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up Subnet Routing
&lt;/h2&gt;

&lt;p&gt;So far, we've built a mesh network where Tailscale devices talk directly using their Tailscale IPs (usually &lt;code&gt;100.x.x.x&lt;/code&gt;). But Headscale and Tailscale can do more. You can let any Tailscale device access an entire local network behind another Tailscale device. This subnet routing has many uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Access your home NAS, printers, and smart devices from anywhere.&lt;/li&gt;
&lt;li&gt;  Connect to internal work servers remotely.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Advanced:&lt;/strong&gt; Access Kubernetes pod and service IPs by making K8s nodes subnet routers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Say you have a Linux machine on your home network (like an OpenWrt router, Raspberry Pi, or any Linux server) running Tailscale and connected to Headscale. You want other Tailscale clients (like your laptop when you're away) to reach any device on your home network using their local IP addresses (like the &lt;code&gt;192.168.100.0/24&lt;/code&gt; subnet).&lt;/p&gt;

&lt;p&gt;Here's how to set up subnet routing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enable IP forwarding on the router:&lt;/strong&gt;&lt;br&gt;
On the Linux machine that will route traffic (like your OpenWrt router), enable IP forwarding:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable IPv4 forwarding&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'net.ipv4.ip_forward = 1'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/sysctl.d/ipforwarding.conf
&lt;span class="c"&gt;# Enable IPv6 forwarding if needed&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'net.ipv6.conf.all.forwarding = 1'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/sysctl.d/ipforwarding.conf
&lt;span class="c"&gt;# Apply changes now&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/sysctl.d/ipforwarding.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Advertise routes from the router:&lt;/strong&gt;&lt;br&gt;
Add &lt;code&gt;--advertise-routes=192.168.100.0/24&lt;/code&gt; to tell Headscale this device can route traffic for that subnet. If Tailscale is already running, use &lt;code&gt;--reset&lt;/code&gt; to apply the new config:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Replace &amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt; with your server address&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale up &lt;span class="nt"&gt;--login-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://&amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt;:8080 &lt;span class="nt"&gt;--accept-routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--accept-dns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--advertise-routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.100.0/24 &lt;span class="nt"&gt;--reset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;For multiple subnets, separate with commas: &lt;code&gt;--advertise-routes=192.168.100.0/24,10.0.0.0/8&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enable the routes on Headscale:&lt;/strong&gt;&lt;br&gt;
After a device advertises routes, you must enable them on the Headscale server before other clients can use them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Headplane:&lt;/strong&gt;&lt;br&gt;
Go to the router device's details page, click "Machine Settings" → "Edit route settings":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-49-b51DzH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-49-b51DzH.png" title="Route settings" alt="Headplane route settings" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find the advertised route (like &lt;code&gt;192.168.100.0/24&lt;/code&gt;) and toggle it to "Enabled":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-50-SAPxRd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2025-05-26-15-50-SAPxRd.png" title="Enable route" alt="Enable route in Headplane" width="800" height="813"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using command line:&lt;/strong&gt;&lt;br&gt;
Find your router node's ID:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;headscale nodes list | &lt;span class="nb"&gt;grep &lt;/span&gt;your-router-hostname
&lt;span class="c"&gt;# Example output:&lt;/span&gt;
&lt;span class="c"&gt;# ID | Hostname             | ...&lt;/span&gt;
&lt;span class="c"&gt;# 6  | openwrt-router       | ...&lt;/span&gt;

&lt;span class="c"&gt;# List routes for the node&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;headscale routes list &lt;span class="nt"&gt;--node-id&lt;/span&gt; 6
&lt;span class="c"&gt;# Example output:&lt;/span&gt;
&lt;span class="c"&gt;# Route            | Enabled | Node&lt;/span&gt;
&lt;span class="c"&gt;# 192.168.100.0/24 | false   | openwrt-router&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Enable the route:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;headscale routes &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--node-id&lt;/span&gt; 6 &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"192.168.100.0/24"&lt;/span&gt;
&lt;span class="c"&gt;# Output: Route enabled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;To enable all routes for a node at once:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;headscale routes &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--node-id&lt;/span&gt; 6 &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Accept routes on client devices:&lt;/strong&gt;&lt;br&gt;
Make sure client devices that need subnet access use &lt;code&gt;--accept-routes=true&lt;/code&gt;. If they're already running without this, reconfigure them with &lt;code&gt;--reset&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On a client that needs subnet access&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale up &lt;span class="nt"&gt;--login-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://&amp;lt;HEADSCALE_PUB_ENDPOINT&amp;gt;:8080 &lt;span class="nt"&gt;--accept-routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--accept-dns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--reset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Check the routing table to confirm it's working:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On a Linux client&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ip route show table 52 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"192.168.100.0/24"&lt;/span&gt;
&lt;span class="c"&gt;# Expected output (gateway IP will be your router's Tailscale IP):&lt;/span&gt;
&lt;span class="c"&gt;# 192.168.100.0/24 via &amp;lt;ROUTING_NODE_TAILSCALE_IP&amp;gt; dev tailscale0 scope link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With subnet routing set up, you can now access any device on your home network's &lt;code&gt;192.168.100.0/24&lt;/code&gt; subnet from any Tailscale client with &lt;code&gt;--accept-routes&lt;/code&gt; enabled. Whether you're at work, a coffee shop, or traveling, you can reach your home devices using their local IP addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Tailscale and Headscale work well for stability, ease of use, and NAT traversal. They beat many older or more complex VPN options. This comes from Tailscale's work on user-space NAT traversal techniques, especially their DERP server network. They wrote a good article about this: &lt;a href="https://tailscale.com/blog/how-nat-traversal-works/" rel="noopener noreferrer"&gt;How NAT traversal works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a simple diagram showing how Tailscale nodes connect, with help from a coordination server (Headscale) and DERP relays for difficult NAT situations:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-03-21-10-52-TzXGEZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.icloudnative.io%2FuPic%2F2022-03-21-10-52-TzXGEZ.png" title="Tailscale/Headscale network connectivity" alt="Tailscale/Headscale network diagram" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide should help you build your own private, secure VPN with Headscale. You get full control over your network.&lt;/p&gt;

&lt;p&gt;My next article will cover how to set up custom DERP servers with Headscale. This will improve connection quality, especially for devices behind difficult firewalls, and give you complete control over your VPN infrastructure.&lt;/p&gt;

</description>
      <category>vpn</category>
      <category>networking</category>
      <category>network</category>
    </item>
    <item>
      <title>Kubernetes CRD Magic: Embedding Terminals Directly on Your Webpage</title>
      <dc:creator> Carson Yang</dc:creator>
      <pubDate>Mon, 04 Sep 2023 07:44:26 +0000</pubDate>
      <link>https://dev.to/carsonyang/kubernetes-crd-magic-embedding-terminals-directly-on-your-webpage-26p6</link>
      <guid>https://dev.to/carsonyang/kubernetes-crd-magic-embedding-terminals-directly-on-your-webpage-26p6</guid>
      <description>&lt;p&gt;In the realm of Kubernetes, utilities such as &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;helm&lt;/code&gt; serve as our principal conduits for cluster interaction. Yet, occasions arise when one might desire to instantiate a terminal directly on a webpage, bypassing the need to embed and configure these instruments locally. This treatise plunges into the intricate details of &lt;strong&gt;procuring this capability via Kubernetes Custom Resource Definitions (CRD)&lt;/strong&gt;, elucidating its architectural and practical facets through a tangible illustration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--paEvuaKL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting5%40main/uPic/2023-08-04-10-17-ZG0cj9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--paEvuaKL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting5%40main/uPic/2023-08-04-10-17-ZG0cj9.jpg" alt="" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The App Launchpad and Database suites within &lt;a href="https://sealos.io"&gt;Sealos&lt;/a&gt; have distilled the Kubernetes resource layer's intricacies into the application stratum. Nonetheless, for more multifaceted situations, a more innate interaction with Kubernetes becomes essential.&lt;/p&gt;

&lt;p&gt;Outlined below is the engagement with the K8s API Server via the Terminal:&lt;/p&gt;

&lt;p&gt;Assessing Pod assets (Kubernetes' elemental scheduling unit, housing the actual container resources):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pP5qgf1Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting5%40main/uPic/2023-08-04-10-18-XjO8Vt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pP5qgf1Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting5%40main/uPic/2023-08-04-10-18-XjO8Vt.jpg" alt="" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Surveying storage pvc resources (container-mounted storage assets, akin to those delineated in App Launchpad):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pvc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sp7mFGLH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting5%40main/uPic/2023-08-04-10-18-79QKjD.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sp7mFGLH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting5%40main/uPic/2023-08-04-10-18-79QKjD.jpg" alt="" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Terminal Endeavors
&lt;/h2&gt;

&lt;p&gt;The Terminal facilitates a plethora of sophisticated tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seamless Integration of Terminal &amp;amp; App Launchpad
&lt;/h3&gt;

&lt;p&gt;Direct entry into each application container's terminal is achievable through the terminal app. Imagine deploying an entity like Nginx under application governance. Navigate to Nginx application's detailed interface, select the ellipsis on the detailed section's rightmost edge, and upon choosing 'Terminal', one is ushered into the Nginx application's terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cVkY8I81--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-09-04-11-31-fqpgrE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cVkY8I81--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-09-04-11-31-fqpgrE.png" alt="" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yP8ETMsD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-09-04-11-33-8u45H9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yP8ETMsD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-09-04-11-33-8u45H9.png" alt="" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Unhindered Bridging of Terminal &amp;amp; Database
&lt;/h3&gt;

&lt;p&gt;Establish an immediate linkage with the database, orchestrated in the &lt;a href="https://docs.sealos.io/docs/platform-components/dbprovider/"&gt;Database App&lt;/a&gt;, right from the terminal.&lt;/p&gt;

&lt;p&gt;Transition to the database's comprehensive view, and opt for the 'connect' to the left, Transition seamlessly to the Terminal and establish a direct connection with the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JrgykeRc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-09-04-11-25-AP2rs3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JrgykeRc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-09-04-11-25-AP2rs3.jpg" alt="" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Dissection
&lt;/h2&gt;

&lt;p&gt;This capability's nucleus is a Kubernetes CRD christened &lt;code&gt;Terminal&lt;/code&gt;. End-users can inaugurate a novel &lt;code&gt;Terminal&lt;/code&gt; CRD on the webpage, post which a new Terminal materializes. This Terminal possesses rights to the designated Kubernetes Namespace, enabling execution of commands such as &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;helm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an archetype of a &lt;code&gt;Terminal&lt;/code&gt; CRD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;apiServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes.default.svc.cluster.local:443&lt;/span&gt;
  &lt;span class="na"&gt;ingressType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;keepalived&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;4h&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xxxxx&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;availableReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://xxxxxx.cloud.sealos.io&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CRD Field Analysis
&lt;/h3&gt;

&lt;p&gt;Within the &lt;code&gt;spec&lt;/code&gt; segment of the &lt;code&gt;Terminal&lt;/code&gt; CRD, each field is delineated as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apiServer&lt;/code&gt;: Kubernetes API server's nexus. Employed by the Terminal for liaisons with the Kubernetes API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ingressType&lt;/code&gt;: Ingress controller variety, either &lt;code&gt;nginx&lt;/code&gt; or &lt;code&gt;apisix&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;keepalived&lt;/code&gt;: Terminal's duration. For instance, &lt;code&gt;4h&lt;/code&gt; denotes an automatic dissolution of the Terminal 4 hours post-inception.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;replicas&lt;/code&gt;: The Terminal's replication count. Presently, only a solitary replica is endorsed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;token&lt;/code&gt;: Authentication token for the Kubernetes API server, harnessed for Terminal's authentication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;status&lt;/code&gt; portion dissects each domain as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;availableReplicas&lt;/code&gt;: The tally of available replications.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;domain&lt;/code&gt;: The nexus employed for web-based Terminal interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon the &lt;code&gt;Terminal&lt;/code&gt; CRD's origination, a fresh Terminal manifests on the webpage. Users are equipped to run commands like &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;helm&lt;/code&gt; within this Terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural Blueprint &amp;amp; Execution
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Terminal&lt;/code&gt; feature's blueprint and realization comprise several pivotal elements:&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal Steward
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Terminal&lt;/code&gt; Steward stands as the linchpin of the &lt;code&gt;Terminal&lt;/code&gt; functionality, vigilantly overseeing the genesis, modification, and dissolution events of the &lt;code&gt;Terminal&lt;/code&gt; CRD and adapting in response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal Capsule
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Terminal&lt;/code&gt; Capsule epitomizes the live Terminal, hosting a bespoke Docker image laden with command-line apparatuses like &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;helm&lt;/code&gt;, alongside a web terminal server, such as &lt;a href="https://github.com/tsl0922/ttyd"&gt;ttyd&lt;/a&gt;. The embedded web terminal server hearkens to port 8080, delivering web terminal services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service &amp;amp; Gateway
&lt;/h3&gt;

&lt;p&gt;Every &lt;code&gt;Terminal&lt;/code&gt; CRD sees the &lt;code&gt;Terminal&lt;/code&gt; Steward instituting a corresponding Kubernetes Service and Gateway. The Service channels traffic to the &lt;code&gt;Terminal&lt;/code&gt; Capsule, while the Gateway navigates external access petitions to the Service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal Docker Imagery
&lt;/h3&gt;

&lt;p&gt;The Docker imagery for the &lt;code&gt;Terminal&lt;/code&gt; Capsule, founded on Ubuntu 20.04, is embellished with command-line tools including &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;helm&lt;/code&gt;, and the ttyd web terminal server. Moreover, it is augmented with MySQL, MongoDB, and Redis clients, facilitating users to connect and govern these databases directly within the Terminal.&lt;/p&gt;

&lt;p&gt;The image's fabrication sequence unfolds as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Integrate vital software packages, spanning &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;helm&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt;, and beyond.&lt;/li&gt;
&lt;li&gt;Implant the web terminal server &lt;code&gt;ttyd&lt;/code&gt; and an initialization script &lt;code&gt;start-terminal.sh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Designate the &lt;code&gt;ttyd&lt;/code&gt; server to port 8080 and calibrate its initiation parameters, which encompass the Kubernetes API server nexus and access token.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Terminal's Dissolution &amp;amp; Longevity
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Terminal&lt;/code&gt; Steward wields Kubernetes' Finalizer mechanism to address the &lt;code&gt;Terminal&lt;/code&gt; CRD's termination events. As a &lt;code&gt;Terminal&lt;/code&gt; CRD meets its end, the Finalizer postpones Kubernetes from immediate eradication, holding out for the &lt;code&gt;Terminal&lt;/code&gt; Steward to clear out resources tied to the &lt;code&gt;Terminal&lt;/code&gt; CRD (like Deployment, Service, and Gateway) before the CRD's removal.&lt;/p&gt;

&lt;p&gt;Furthermore, the &lt;code&gt;Terminal&lt;/code&gt; Steward capitalizes on the Keepalive mechanism, autonomously eliminating lapsed &lt;code&gt;Terminal&lt;/code&gt; entities. The &lt;code&gt;Terminal&lt;/code&gt; longevity, designated by the &lt;code&gt;keepalived&lt;/code&gt; field, triggers its erasure once its existence surpasses the stipulated &lt;code&gt;keepalived&lt;/code&gt; duration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interaction Schema Between the User Interface and Backend Systems
&lt;/h2&gt;

&lt;p&gt;Outlined below is the meticulous progression from the moment a user engages the Terminal button to when they gain access to the Terminal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Users activate the Terminal button on a web interface, instigating a backend request. This petition encompasses details regarding the Terminal's configuration, such as its designated Kubernetes Namespace and predetermined lifespan.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upon assimilation of the request, the backend system spawns a novel &lt;code&gt;Terminal&lt;/code&gt; CRD (Custom Resource Definition) infused with the Terminal's specific configuration attributes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The vigilant &lt;code&gt;Terminal&lt;/code&gt; Controller, upon discerning the genesis of a new &lt;code&gt;Terminal&lt;/code&gt; CRD, orchestrates the appropriate &lt;code&gt;Terminal&lt;/code&gt; Pod. This specific Pod operates a Docker image replete with command-line tools like &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;helm&lt;/code&gt;, and an embedded web terminal server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concurrently, the &lt;code&gt;Terminal&lt;/code&gt; Controller commissions a corresponding Kubernetes Service and Ingress. This Service navigates network traffic towards the &lt;code&gt;Terminal&lt;/code&gt; Pod, whilst the Ingress directs external solicitations to the said Service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Posthaste, the backend system extracts the Terminal's domain information from the &lt;code&gt;status&lt;/code&gt; segment of the &lt;code&gt;Terminal&lt;/code&gt; CRD, relaying this domain to the frontend system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upon reception of the Terminal's domain, the frontend system projects it onto a fresh browser tab, furnishing users with a pristine Terminal. Within this environment, users are equipped to invoke commands such as &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;helm&lt;/code&gt;, and more.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tailored Configurations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Assorted Ingress Controller Compatibility
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Terminal&lt;/code&gt; is adept at liaising with a plethora of Ingress Controllers, notably Nginx and Apisix. Users are thus empowered to elect an Ingress Controller that resonates with their distinct requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adjustable Lifespan Setting
&lt;/h3&gt;

&lt;p&gt;End-users possess the prerogative to delineate the Terminal's duration of existence. Post a specified interval subsequent to its creation, the &lt;code&gt;Terminal&lt;/code&gt; will be autonomously terminated. This strategy circumvents the undue occupation of resources by dormant Terminals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prospective Enhancements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Augmented Command-line Instrument Integration
&lt;/h3&gt;

&lt;p&gt;Anticipated versions shall incorporate an expansive set of command-line tools within the &lt;code&gt;Terminal&lt;/code&gt; Docker image, encompassing tools like &lt;code&gt;istioctl&lt;/code&gt;, &lt;code&gt;kn&lt;/code&gt;, and beyond, bestowing users with a more extensive operational gamut.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expanded Ingress Controller Affiliation
&lt;/h3&gt;

&lt;p&gt;There are blueprints to embrace supplementary Ingress Controllers such as Traefik and HAProxy, thereby diversifying users' available choices.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSocket Channelization
&lt;/h3&gt;

&lt;p&gt;The inauguration of a WebSocket service through Ingress is cardinal. This facilitates users to inaugurate a terminal within the web facade and liaise with the Kubernetes cluster via WebSockets. When juxtaposed against HTTP, WebSockets proffer a superior real-time bi-directional communication channel, amplifying user engagement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Granulated Access Governance
&lt;/h3&gt;

&lt;p&gt;The incorporation of a refined access governance mechanism is paramount. With imminent enterprise collaborative modules, a myriad of users could coalesce within namespaces. Consequently, the terminal will access distinct user realms—be it managerial or developmental—relying on the permissions procured.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assimilation of Diverse Development Apparatuses
&lt;/h3&gt;

&lt;p&gt;Beyond the realms of &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;helm&lt;/code&gt;, the Terminal aspires to amalgamate a variety of developmental and debugging tools, including but not limited to &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;, and so forth.&lt;/p&gt;

&lt;h3&gt;
  
  
  User-Centric Customizations
&lt;/h3&gt;

&lt;p&gt;End-users will be capacitated to modify the Terminal's visual aesthetics, such as chromatic themes, typographic dimensions, and more. Additionally, they can calibrate behavioral configurations like the archival depth of command histories, tactile shortcuts, among others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Epilogue
&lt;/h2&gt;

&lt;p&gt;Leveraging Kubernetes' CRD capabilities, we can seamlessly induct a robust Terminal within a web framework. Within this Terminal, users can seamlessly deploy an array of commands, fostering a more intimate rapport with the Kubernetes cluster. This not only optimizes their procedural workflows but also profoundly elevates their overarching interaction experience.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>containers</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Revolutionizing Database Services: An Inside Look at Sealos and KubeBlocks</title>
      <dc:creator> Carson Yang</dc:creator>
      <pubDate>Tue, 29 Aug 2023 10:55:05 +0000</pubDate>
      <link>https://dev.to/carsonyang/revolutionizing-database-services-an-inside-look-at-sealos-and-kubeblocks-3p83</link>
      <guid>https://dev.to/carsonyang/revolutionizing-database-services-an-inside-look-at-sealos-and-kubeblocks-3p83</guid>
      <description>&lt;p&gt;Greetings to all readers! Today, we present an exquisite overview of the database services rendered by &lt;a href="https://sealos.io"&gt;Sealos&lt;/a&gt;. KubeBlocks, an esteemed backend service provider, assures uninterrupted functioning of your database applications on Sealos. Regardless of employing it in a public domain or a local setup, Sealos deftly manages relational databases, NoSQL, vector databases, and streaming databases. This platform is &lt;strong&gt;tailored for professional environments&lt;/strong&gt;, guaranteeing dependable, top-tier, transparent, and economically viable data infrastructure.&lt;/p&gt;

&lt;p&gt;Let us embark on an exploration of the allure of database applications:&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Database Genesis
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://sealos.io"&gt;Sealos&lt;/a&gt; presents an intuitive database frontend platform, effectively obviating intricate command-line tasks. Via this platform, one can effortlessly instantiate diverse database categories, encompassing Mysql, PostgreSQL, MongoDB, Redis, and an expansive list beyond these.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TUOD4Jdu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-29-16-07-d0IjL1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TUOD4Jdu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-29-16-07-d0IjL1.jpeg" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2xZ89h0N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-29-16-08-fCoEZu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2xZ89h0N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-29-16-08-fCoEZu.png" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection Dynamics
&lt;/h3&gt;

&lt;p&gt;Upon entering the specifics page, one can scrutinize comprehensive database information and engage with it via a singular connection action. This streamlines procedures, augmenting your database interactivity quotient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bi5BuRxE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/Q24guNjrGcmen7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bi5BuRxE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/Q24guNjrGcmen7q.png" alt="" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Assured Uptime Examination
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://cloud.sealos.io"&gt;Sealos&lt;/a&gt; shines in bestowing unparalleled uptime services, fortifying databases against untimely disruptions. This confers an augmented security layer to your repositories, facilitating a tranquil utilization. Our assertion of database resilience is corroborated with a succinct demonstration.&lt;/p&gt;

&lt;p&gt;To commence, instantiate a database application with plural instances; in this case, a 3-instance PostGreSQL. Post-instantiation, one can interface with the database command terminal using the singular connection feature. We activate the &lt;code&gt;Add a Terminal&lt;/code&gt; function to instantiate a secondary terminal module.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SkZMHcc7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/SlJ4ojHNbEOxWQP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SkZMHcc7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/SlJ4ojHNbEOxWQP.png" alt="" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Subsequently, we identify &lt;code&gt;test-postgresql-1&lt;/code&gt; as the reigning master node. We then expeditiously remove this principal node employing the &lt;code&gt;kubectl delete pod test-postgresql-1&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j5VoUJh_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-DduTby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j5VoUJh_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-DduTby.png" alt="" width="775" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZT6j1uw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-vof083.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZT6j1uw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-vof083.png" alt="" width="784" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Restoration Capabilities
&lt;/h3&gt;

&lt;p&gt;Our database also boasts a formidable backup and revival feature, assuring that even amidst data compromisation or loss, restoration to the backed-up state is effortless. Be it inadvertent deletions or calamitous events, these backups ensure rapid database reconstitution, curtailing operational stagnation.&lt;/p&gt;

&lt;p&gt;For illustration, I have pre-curated data. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---j7zTIIJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/xNQkay3TJBXMUnG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---j7zTIIJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/xNQkay3TJBXMUnG.png" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the backup archive and activate the "Backup" button to commence the archival process. Herein, the manual archival method is showcased. Backup titles and annotations are modifiable; post modifications, one simply engages the "Start Backup" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s3zebrRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/HRXJw1emyxqOtfT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s3zebrRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/HRXJw1emyxqOtfT.png" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the backup is completed, you can check the results through its status. You can click the "restore" icon to initiate the recovery process and wait for the restored database to successfully start. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ry2VVVH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/KVN3XxvhE7Smeps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ry2VVVH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/KVN3XxvhE7Smeps.png" alt="" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the restored database to view the data, and you will find the data successfully recovered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surveillance Capabilities
&lt;/h3&gt;

&lt;p&gt;Within Sealos, beyond mere database management, you’re granted access to intuitive monitoring utilities. The operational status of your database is always at your fingertips, and its efficiency can be ascertained through graphical representations and metrics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cwPfWofG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/HYpPiSuIlcQ3mqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cwPfWofG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/08/29/HYpPiSuIlcQ3mqj.png" alt="" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Functional Underpinnings
&lt;/h2&gt;

&lt;p&gt;The prowess of KubeBlocks in proffering steadfast and adept database services on Sealos may pique your curiosity. Let's journey into its operational paradigms and fathom its resilient architecture and data recovery stratagems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design Paradigm for Stateful Services
&lt;/h3&gt;

&lt;p&gt;KubeBlocks adeptly architects database topologies. With the Replicated State Machine (RSM) and its replication interrelations as its nucleus, it morphs intricate distributed databases into integrated foundational modules (blocks). Stateless, StatefulSet, or RSM, all seamlessly integrate within KubeBlocks. This facilitates the amalgamation of any database engine with KubeBlocks, birthing a dynamic system framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2PZZ2GQo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-UZ2vKR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2PZZ2GQo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-UZ2vKR.png" alt="" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilient Architectural Blueprint
&lt;/h3&gt;

&lt;p&gt;KubeBlocks inculcates dual resilience algorithms: consensus-driven and traditional primary-secondary. In consensus-driven databases, KubeBlocks oversees role discernment, rectification, and restructuring. The database autonomously manages thorough detection, adjudication, and transition tasks. For archetypal primary-secondary contexts, like MySQL and PostgreSQL pairings, KubeBlocks supervises detection, decision-making, transition, reconstruction, and role rectification. Moreover, its universal resilience blueprint accommodates myriad database categories such as replicated clusters and segmented clusters, amplifying reliability by enhancing replica counts and diminishing data loss chances.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pxpl0z8L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-jviXWu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pxpl0z8L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-jviXWu.png" alt="" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rfrnRGdz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-8mGn9Z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rfrnRGdz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-8mGn9Z.png" alt="" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Salvage Blueprint
&lt;/h3&gt;

&lt;p&gt;To bolster data safety, KubeBlocks introduces a salvage and revival strategy. Backups cater to diverse scenarios such as data misplacement, calamity recovery, and data transference. On Sealos, a tangible backup feature is offered, signifying a direct replication of the database's corporeal files (both data and log) onto backup mediums like storage disks or tapes. These tangible backups are marked by swift restorations, consistent data, and storage optimization. Their efficiency in restoring backup files to the original database, preserving physical congruence with the database, and conserving storage space is unmatched. This makes it a premier choice for substantial databases, further elevating Sealos's prowess for professional settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yT5_7iT8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-yImvhE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yT5_7iT8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-17-23-28-yImvhE.png" alt="" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For those captivated by database solutions, &lt;a href="https://sealos.io"&gt;Sealos&lt;/a&gt; is a commendable platform to explore. Whether a developer, data maestro, or systems curator, this robust database service promises enhanced data management and operations. Let Sealos elevate your data endeavors to celestial heights!&lt;/p&gt;

&lt;p&gt;Do not falter; &lt;a href="https://sealos.io"&gt;experience it firsthand&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cloud</category>
      <category>mysql</category>
      <category>database</category>
    </item>
    <item>
      <title>Sealos Cloud Operating System — Simplicity, Affordability, and Liberation in Cloud Management</title>
      <dc:creator> Carson Yang</dc:creator>
      <pubDate>Mon, 21 Aug 2023 09:30:04 +0000</pubDate>
      <link>https://dev.to/carsonyang/sealos-cloud-operating-system-simplicity-affordability-and-liberation-in-cloud-management-2ld8</link>
      <guid>https://dev.to/carsonyang/sealos-cloud-operating-system-simplicity-affordability-and-liberation-in-cloud-management-2ld8</guid>
      <description>&lt;h2&gt;
  
  
  A Grand Vision
&lt;/h2&gt;

&lt;p&gt;It is a magnificent and captivating plan. &lt;/p&gt;

&lt;p&gt;One quiet night in 2018, I sat at my keyboard and typed the first line of code for Sealos. Initially, the repository was named "kubeinit," but I soon realized that the scope was too narrow. I couldn't just create a tool for installing Kubernetes. Installation was merely a piece of a larger puzzle. Thus, it was renamed &lt;a href="https://github.com/labring/sealos/"&gt;Sealos&lt;/a&gt;, and a grand cloud operating system vision was conceived!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--57I9JZ8z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-15-rcAujq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--57I9JZ8z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-15-rcAujq.webp" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After completing the first version of Sealos, I released it for sale on the Alibaba Cloud Marketplace for &lt;strong&gt;15 yuan per copy&lt;/strong&gt;. I never anticipated that people would actually purchase it. When I received the first 15 yuan payment, I was incredibly excited, as if a business empire was unfolding before my eyes. However, the reality was that I spent an entire day providing after-sales service to that customer... I was still resolving issues for users even while at the cinema!&lt;/p&gt;

&lt;p&gt;Soon, sales soared, and I quickly upgraded to a new iPhone 8. However, the number of issues also increased simultaneously, to the point where I couldn't provide timely after-sales support to everyone. So, I decided to rewrite Sealos and released version 2 based on Ansible. But in the end, I felt that I hadn't achieved perfection because users still faced too many dependency-related problems. It wasn't until I finished reading the source code of kube-proxy that I discovered a solution to simplify load balancing and &lt;strong&gt;eliminate all dependencies&lt;/strong&gt;. That's when I developed Sealos version 3, which achieved the pinnacle of installation simplicity. &lt;/p&gt;

&lt;h3&gt;
  
  
  Why focus on installation initially?
&lt;/h3&gt;

&lt;p&gt;Installation is the entry point, and most people learning cloud-native technologies cannot avoid this issue. The flow of installation is significant, making it an excellent starting point. Once users become accustomed to using Sealos for installation, they will gradually explore its other functionalities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working at Alibaba
&lt;/h3&gt;

&lt;p&gt;During my time at Alibaba, I developed Sealer. The most important aspect here was to make installation highly flexible. Previously, users could only use the installation package I created, but the innovation of cluster imaging allows users to define their own installation packages and freely combine any packages. Here's a thought that makes me proud: &lt;strong&gt;If we consider the entire cluster as a whole and view Kubernetes as an operating system, what would a "cloud version Docker image" look like within this cloud operating system?&lt;/strong&gt; Undoubtedly, this is a great concept that offers high levels of abstraction and flexibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dockerfile
FROM kubernetes:v1.25.0
COPY mysql .
CMD helm install mysql .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This concept allows the cloud operating system to have "images" just like a standalone operating system. Another step is completed in this grand vision. &lt;/p&gt;

&lt;h2&gt;
  
  
  The First Year of Entrepreneurship
&lt;/h2&gt;

&lt;p&gt;So, what will the Sealos cloud operating system ultimately become? That is an indescribable question, and I only have a vague vision of it. It wasn't until the continuous iteration of three versions during the entrepreneurial process that it took on its present form—&lt;strong&gt;Everything is an application!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Understanding this is actually quite simple. Just replace the standalone applications installed on a single machine operating system with various distributed applications. The entire data center will no longer appear as isolated servers but as a unified whole, transforming into a virtual supercomputer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VFuq-XBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-30-Zn4l1W.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VFuq-XBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-30-Zn4l1W.webp" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This sleek, refreshing, and near-perfect cloud operating system is bound to capture your heart from the moment you lay eyes on it!  &lt;/p&gt;

&lt;p&gt;This is my painstaking work of five years—Sealos! Dedicated to all of you~&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cloud Can Be So Clean
&lt;/h2&gt;

&lt;p&gt;Sealos maintains an extremely minimalist design with no unnecessary buttons. It achieves simplicity and powerful parallel functionality, which is sometimes as difficult as reaching the heavens. Nevertheless, we have put a great deal of effort into the product design. Regardless of who you are, using Sealos will immerse you in the comfortable experience we have crafted.&lt;br&gt;
In the world of B2B software, the payer and the user are often not the same person, resulting in a neglect of the user experience. The most crucial thing is to convince decision-makers. However, Sealos is different. We firmly believe that the user experience surpasses everything else. If we dedicate a significant amount of energy to the product and ultimately fail, we will have no regrets. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---iR-zNL6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-31-jqkByJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---iR-zNL6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-31-jqkByJ.png" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The black, white, and gray design style will make you feel like you're drinking plain water while using the product, rather than a beverage, let alone footwash (as some products make you feel like dying). Developers already suffer enough, and I hope that using Sealos will bring you a pleasant mood.  &lt;/p&gt;

&lt;p&gt;Sealos can pinpoint the pain points of applications. For example, the App Launchpad, an application manager, allows you to launch your own application within 30 seconds. This involves numerous details, such as automatically configuring public domain names and resolving HTTPS certificate issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cloud Can Be So Affordable?
&lt;/h2&gt;

&lt;p&gt;I have run over ten applications on Sealos, including three databases, a blog, a low-code platform, a testing platform, and more, all costing me only 4 yuan per day:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gOD28tr6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-34-NmR7oB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gOD28tr6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-34-NmR7oB.png" alt="" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why is it so affordable?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You only need to pay for the running containers, without the need for virtual machines or creating an entire Kubernetes cluster. It's open and ready to use.&lt;/li&gt;
&lt;li&gt;Automatic scaling reduces the number of replicas to 1 during periods of low user traffic. &lt;/li&gt;
&lt;li&gt;We can make full use of the elasticity of public clouds, write a significant amount of automation code, release computing resources during nighttime, and reduce costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For enterprises, this can significantly reduce resource utilization costs. We ourselves run over 7,000 applications on just 10 servers. What does that mean? After deploying a Sealos cluster, as long as the server resource utilization is below 70%, you can continuously add applications to the cluster until it reaches its capacity. &lt;/p&gt;

&lt;p&gt;You might wonder, &lt;strong&gt;why not use Kubernetes directly?&lt;/strong&gt; The reason is simple. For enterprises like Xunfei, applications are distributed across various departments, making multi-tenancy, isolation, and collaboration crucial. Using Kubernetes directly could disrupt the cluster, and the worst-case scenario is that a department or user inadvertently causes a security issue that crashes the entire cluster. Sealos perfectly solves this problem!&lt;/p&gt;

&lt;p&gt;Sealos can help 80% of enterprises reduce their resource utilization costs by 80%.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cloud Can Be So Liberating
&lt;/h2&gt;

&lt;p&gt;Unlike other management platforms or PaaS platforms, the core design principle of Sealos is "everything is an application." Different developers with different roles can use different applications, allowing each user to use the platform without any mental burden. It's similar to the Android ecosystem, where there are millions of applications, but you only care about the ones you use without worrying about what other applications are doing.&lt;/p&gt;

&lt;p&gt;This design has two main advantages:&lt;/p&gt;

&lt;h3&gt;
  
  
  Seamlessly Use Sealos Whether You Understand Kubernetes or Not
&lt;/h3&gt;

&lt;p&gt;Many Kubernetes-based PaaS platforms or distributions either expose a large number of native Kubernetes concepts or hide these concepts. Neither approach is ideal. &lt;/p&gt;

&lt;p&gt;Exposing a large number of native concepts is unfriendly to beginners and novices, while hiding Kubernetes loses flexibility and compatibility, making it unfriendly for Kubernetes experts.&lt;/p&gt;

&lt;p&gt;Sealos takes a different approach. On this platform, different people can use different applications. For example, if you're a developer who wants to write CRUD operations, you can directly use the Laf function application. If you're a DBA, you can directly use the database application. In this case, you don't need to care about Kubernetes at all as these concepts are completely abstracted. &lt;/p&gt;

&lt;p&gt;If users are cloud-native experts, they can install Lens and various Kubernetes dashboards on Sealos. They can also open a terminal and use native commands. This greatly enhances flexibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Freedom to Assemble
&lt;/h3&gt;

&lt;p&gt;Sealos pays great attention to the coordination between applications. For example, if you're using function compute on Sealos, the default database might be MongoDB. But what if you want to use PostgreSQL? In this case, you can install a PostgreSQL application on Sealos and access it directly within the function compute through service discovery. Since they are in the same cluster, they can directly communicate through internal DNS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Xj4ybg0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-37-5VOWuC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Xj4ybg0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.jsdelivr.us/gh/yangchuansheng/imghosting-test%40main/uPic/2023-08-21-15-37-5VOWuC.png" alt="" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sealos is streamlined yet not simplistic. All components can be uninstalled, allowing the cloud to perfectly meet your needs—more is considered excessive, less is considered insufficient. This also means that whether it's a single server or hundreds of data centers, they can be built into a cloud with just one command.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can Sealos Actually Do?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run an nginx demo on Sealos in just 30 seconds with automatic scaling.&lt;/li&gt;
&lt;li&gt;Start various databases in 30 seconds and connect to them directly within your business system's intranet.
&lt;/li&gt;
&lt;li&gt;Launch your business applications written in various programming languages directly on Sealos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These three capabilities serve as the foundation, and you can gradually explore and discover new territories. &lt;/p&gt;

&lt;p&gt;When it comes to running your own business, we have made many detailed optimizations for this scenario. For example, automatic allocation of subdomains, horizontal autoscaling, and support for running various stateful services.&lt;/p&gt;

&lt;p&gt;You will find that with Sealos, &lt;strong&gt;whether you're deploying a monitoring system or running a low-code platform, it's all within reach. You can easily host your blog on Sealos at a low cost. Using the Sealos terminal, you can run any Kubernetes-compatible application without difficulty in automation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Moreover, you'll discover that there is actually &lt;strong&gt;an AI helping you with automated fault diagnosis, automatic deployment of services, and even assisting you in code writing and automatic testing for deployment&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Then you'll realize that even ordinary people can use Sealos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can quickly install financial software on Sealos.&lt;/li&gt;
&lt;li&gt;You can also quickly install a knowledge base on Sealos to write notes for everyone in the company.&lt;/li&gt;
&lt;li&gt;You can even install a chat software on Sealos for internal communication and collaboration within the enterprise.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, you'll be pleasantly surprised to discover that Sealos can do everything, truly versatile, and yet so simple! Ultimately, you'll come to understand that &lt;strong&gt;this is what a cloud operating system is!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Are People Really Using Sealos?
&lt;/h2&gt;

&lt;p&gt;Absolutely, the Sealos community has over 100,000 users, including various large enterprises.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dSR_l3xb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/06/01/NwG4LVFs8SDvhlA.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dSR_l3xb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://s2.loli.net/2023/06/01/NwG4LVFs8SDvhlA.jpg" alt="Sealos Community" width="800" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within two months of its launch, Sealos has already surpassed 10,000 registered users, with a total of &lt;strong&gt;7,000+ applications&lt;/strong&gt; running on the cloud service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Sealos only suitable for small-scale applications?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Certainly not. Among Sealos' customers is the National Health Big Data platform, which supported the health code service during the pandemic with high concurrency business that couldn't afford a single second of downtime. Sealos has also supported large-scale GPU clusters that process 80 terabytes of data daily, with a total of 80 petabytes of data in the entire cluster. The Judo Cloud platform runs hundreds of applications on Sealos.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Roadmap
&lt;/h2&gt;

&lt;p&gt;Sealos' grand vision goes beyond what has been accomplished so far. Our goal is to evolve into an omnipresent cloud operating system, providing people with a user-friendly cloud service experience akin to using a personal computer. With Sealos, enterprises can effortlessly achieve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rapid deployment of new businesses in just a minute.&lt;/li&gt;
&lt;li&gt;A 50% reduction in costs within a year.
&lt;/li&gt;
&lt;li&gt;One-click creation of a cloud, as simple as flipping a switch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For enterprise cloud needs, Sealos is the solution. &lt;/p&gt;

&lt;p&gt;In the future, we will continue to uphold the spirit of craftsmanship and carefully craft common applications that enterprises require within the Sealos cloud operating system, such as databases, message queues, inference capabilities, and various programming language execution environments.&lt;/p&gt;

&lt;p&gt;The Sealos cloud operating system will also incorporate a &lt;strong&gt;Copilot&lt;/strong&gt;, acting as a navigator's assistant. It can automatically perform cloud-native transformations, helping developers easily enter the realm of cloud-native. It can also assist in diagnosing cluster issues, identifying security vulnerabilities, and providing professional operational advice like an expert.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;After five years, Sealos has finally realized the vision I had when I wrote the first line of code—a cloud operating system.&lt;/p&gt;

&lt;p&gt;I am grateful to the first person who paid me 15 yuan. Your trust and encouragement have been like a huge investment, giving me the strength to move forward. &lt;/p&gt;

&lt;p&gt;I want to express my gratitude to all the contributors in the community, especially my fellow companion, Lao Cui, who has been with me throughout the journey.&lt;/p&gt;

&lt;p&gt;Special thanks to Xunfei for refining complex business scenarios, which deepened my understanding of business scenarios.&lt;/p&gt;

&lt;p&gt;I appreciate the support and assistance from Alibaba Cloud while developing Sealer. It laid a solid foundation for the underlying capabilities of the Sealos cluster image.&lt;/p&gt;

&lt;p&gt;I am thankful to all the partners who embarked on this entrepreneurial journey with me. It is through everyone's efforts that the seed of inspiration has grown into a tangible reality.  &lt;/p&gt;

&lt;p&gt;I want to thank Qi Jia Chuang Tan for giving us the unexpected push, and most importantly, &lt;strong&gt;thank you Dr. Lu Qi&lt;/strong&gt; for your unwavering confidence, which has given us great encouragement.&lt;/p&gt;

&lt;p&gt;Thanks to Professor Li Jun, Dean Kang Yi, Professor Zhang Hailong, Gaojie Capital, and Jinfu Asset for their help, guidance, and trust. &lt;/p&gt;

&lt;p&gt;I express my gratitude to every user who chose Sealos. Your understanding and patience throughout the iterative process have shaped a more perfect Sealos together with us.&lt;/p&gt;

&lt;p&gt;To all the decision-makers who have chosen us, I wish to congratulate you on making a wise decision. Sealos has now reached a new starting point, and we will undoubtedly meet your expectations and deliver a perfect cloud operating system.  &lt;/p&gt;

&lt;p&gt;You are welcome to experience the charm of the Sealos cloud operating system at &lt;a href="https://cloud.sealos.io/"&gt;https://cloud.sealos.io/&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Boost Your Coding Efficiency with the Laf Assistant in VS Code</title>
      <dc:creator> Carson Yang</dc:creator>
      <pubDate>Thu, 20 Apr 2023 03:49:22 +0000</pubDate>
      <link>https://dev.to/carsonyang/boost-your-coding-efficiency-with-the-laf-assistant-in-vs-code-28h9</link>
      <guid>https://dev.to/carsonyang/boost-your-coding-efficiency-with-the-laf-assistant-in-vs-code-28h9</guid>
      <description>&lt;h1&gt;
  
  
  Boost Your Coding Efficiency with the Laf Assistant in VS Code
&lt;/h1&gt;

&lt;p&gt;To excel in one's work, one must first sharpen one's tools. In the realm of coding, an indispensable component is the Integrated Development Environment (IDE), which enables us to write code with greater efficiency and expedites the development process. Thus, the IDE becomes one of our most intimate companions in coding.&lt;/p&gt;

&lt;p&gt;Although Laf Cloud Development offers a minimalistic Web IDE, enabling users to edit cloud functions anywhere with a simple browser,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C7654ops--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-12-glDx46.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C7654ops--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-12-glDx46.png" alt="" width="800" height="645"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;many individuals still prefer to write cloud function code within an IDE.&lt;/p&gt;

&lt;p&gt;Thanks to the development of laf-cli and the openness of APIs, Laf imposes no mandatory requirements on local code editors, which signifies that Laf can be used directly with VS Code or any other editor to compose, debug, publish, and update cloud functions.&lt;/p&gt;

&lt;p&gt;laf-cli is an npm module that facilitates cloud function management, cloud storage management, and more for Laf Cloud Development via the command line. Utilizing the command line, I have created a VS Code extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=NightWhite.laf-assistant"&gt;laf assistant&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The experience of using Laf directly with VS Code is not flawless
&lt;/h2&gt;

&lt;p&gt;VS Code is a free, open-source, cross-platform code editor developed and maintained by Microsoft, boasting an abundance of plugins and powerful features. Importantly, I am personally accustomed to using VS Code, as are a substantial number of developers.&lt;/p&gt;

&lt;p&gt;Prior to the emergence of the laf assistant, I had experimented with the Laf-cli module for some time. Upon modifying a code segment and needing to debug it, I had to input rather complex commands.&lt;/p&gt;

&lt;p&gt;For instance, if the cloud function is named "BatchTestDB," I would have to enter the command line: &lt;code&gt;laf func exec BatchTestDB -l 9&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ZhwOxXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-10-3lJDdW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ZhwOxXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-10-3lJDdW.png" alt="" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Switching cloud functions necessitates manual command modification. When confronted with copious logs, the experience is as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fwt5geAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-11-a2iwIM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fwt5geAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-11-a2iwIM.png" alt="" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another cumbersome issue is that the content output in the terminal is uneditable and lacks code folding. Inspecting extensive JSON data logs can be a particularly agonizing endeavor. As debugging progresses over time, the terminal becomes increasingly cluttered and overwhelming.&lt;/p&gt;

&lt;p&gt;When it comes time to publish cloud functions, manual command line modification is once again required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GQbttvBm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-11-xXBoHV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GQbttvBm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-11-xXBoHV.png" alt="" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Laf Cloud Development's Web IDE is evidently more convenient and straightforward for debugging, publishing, and managing cloud functions than this approach. To seamlessly combine the outstanding advantages of the Web IDE with local development, I have contemplated developing a VS Code extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code-writing Experience of &lt;code&gt;Laf assistant&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v328_BJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-12-Qrk0uv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v328_BJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-12-Qrk0uv.png" alt="" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within the cloud function editor, right-click to publish, download, or execute cloud functions. Combined with VS Code's native remapping capabilities, I have remapped running cloud functions to &lt;strong&gt;⌘+F1&lt;/strong&gt;. Upon modifying and saving a cloud function, simply press &lt;strong&gt;⌘+F1&lt;/strong&gt; to execute it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cEPEcaEC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-12-JLL2kc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cEPEcaEC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-04-19-20-12-JLL2kc.png" alt="" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have directed log output to a new, editable, and foldable text file for increased convenience. If necessary, the log can be saved directly to a file, or the editor can be closed to discard the log content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Version Control and Collaboration
&lt;/h2&gt;

&lt;p&gt;Laf Cloud Development's Web IDE lacks a code system and version control. The Web IDE does not save all modified versions; instead, it retains them in the browser's local cache. This implies that inadvertent cache clearing or browser switching may result in the loss of drafts within the Web IDE editor. Collaborative development in multi-person projects can also be cumbersome.&lt;/p&gt;

&lt;p&gt;In my opinion, the Web IDE is well-suited for rapid development and deployment of simple cloud functions, as well as temporary code modifications. However, in complex code situations, it cannot compare to the local development experience of VS Code.&lt;/p&gt;

&lt;p&gt;Additionally, &lt;code&gt;laf assistant&lt;/code&gt; allows management of frontend code and Laf cloud function code within a single project, facilitating comprehensive management of frontend and backend code. The Web IDE is also incapable of searching code, whereas having frontend and backend code in one project enables easy global searching.&lt;/p&gt;

&lt;p&gt;When used in conjunction with Git, version control can be conveniently implemented.&lt;/p&gt;

&lt;p&gt;Moreover, for collaborative development, &lt;code&gt;laf assistant&lt;/code&gt; supports individual configuration of Laf API addresses and Laf application Appids for the same project on different computers. Each developer can debug and develop within their own Laf application, and upon code deployment, publish to the official application, thus avoiding test data contamination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subsequent Upgrade Plans for &lt;code&gt;laf assistant&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. AI Empowerment
&lt;/h3&gt;

&lt;p&gt;The interface has been essentially implemented. With a&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7UB6swID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://files.mdnice.com/user/2166/550787c6-7a89-4e09-812f-5084bdd16b71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7UB6swID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://files.mdnice.com/user/2166/550787c6-7a89-4e09-812f-5084bdd16b71.png" alt="" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the future, within VS Code, AI will be able to automatically complete cloud function code with just a few clicks or keystrokes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Enhance Cloud Storage Management
&lt;/h3&gt;

&lt;p&gt;Locally compiled frontend code can be hosted in Laf's cloud storage with a single click.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Addition of More Debugging Modes for Cloud Functions
&lt;/h3&gt;

&lt;p&gt;Custom debugging parameters will be available. Subsequent updates will also include real-time log monitoring, facilitating frontend debugging and log inspection.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Standalone Management Panel
&lt;/h3&gt;

&lt;p&gt;By clicking the Laf icon in the sidebar, all Laf Cloud Development-related content will be centralized and displayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Further Detail Optimization
&lt;/h3&gt;

&lt;p&gt;All developers are encouraged to provide suggestions for improvement, making &lt;code&gt;laf-assistant&lt;/code&gt; even more user-friendly and invigorating the cloud development experience!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>chatgpt</category>
      <category>ai</category>
    </item>
    <item>
      <title>Construct a ChatGPT web demonstration page using Laf</title>
      <dc:creator> Carson Yang</dc:creator>
      <pubDate>Mon, 27 Mar 2023 12:36:25 +0000</pubDate>
      <link>https://dev.to/carsonyang/construct-a-chatgpt-web-demonstration-page-using-laf-5359</link>
      <guid>https://dev.to/carsonyang/construct-a-chatgpt-web-demonstration-page-using-laf-5359</guid>
      <description>&lt;p&gt;OpenAI has released the official ChatGPT API, powered by their newest model gpt-3.5-turbo, which is their most advanced model yet, with faster response times and cheaper pricing.&lt;/p&gt;

&lt;p&gt;As developers, we want to integrate ChatGPT and related models into our own products and applications through the API. However, due to access restrictions, we need to use various API proxy services to access the ChatGPT API indirectly.&lt;/p&gt;

&lt;p&gt;Even if we solve the API access problem, we still need to prepare a development environment, such as a Node.js environment for a Node.js client.&lt;/p&gt;

&lt;p&gt;Is there a simple and fast way to call the ChatGPT API?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can use Laf.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Laf is a fully open-source cloud development platform that provides out-of-the-box capabilities such as cloud functions, cloud databases, and object storage, allowing you to write code like writing a blog post.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GitHub: &lt;strong&gt;&lt;a href="https://github.com/labring/laf"&gt;https://github.com/labring/laf&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to quickly learn how to use Laf, you can refer to this article: &lt;a href="https://zuofeng59556.github.io/my-blog/pages/quickStart/laf/"&gt;Learn Laf in Three Minutes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's start the timer and use Laf to implement our own ChatGPT in three minutes!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prerequisite: You need to have a ChatGPT account and generate an API Key (you can search on Google for this).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cloud Function Tutorial
&lt;/h2&gt;

&lt;p&gt;First, log in to &lt;a href="https://laf.dev/"&gt;laf.dev&lt;/a&gt; and create a new application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3uX1CZ3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-39-hzELHb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3uX1CZ3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-39-hzELHb.png" alt="" width="800" height="1011"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the "Develop" button to enter the development page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w3Fqd4uR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-42-UwEWtp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w3Fqd4uR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-42-UwEWtp.png" alt="" width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the NPM Dependence panel, click the &lt;code&gt;+&lt;/code&gt; in the upper right corner:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AxBX0YEl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-46-fUtTj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AxBX0YEl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-46-fUtTj2.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then enter "chatgpt" and press enter to search, select the first search result, and click "save and restart":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GoBrw_uh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-47-h1zrwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GoBrw_uh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-47-h1zrwt.png" alt="" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the restart, "chatgpt" will appear in the custom dependency items.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1IzopCUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-48-ToRLw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1IzopCUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-48-ToRLw5.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, create a new cloud function named &lt;strong&gt;send&lt;/strong&gt; and write the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lafjs/cloud&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatGPTAPI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chatgpt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ChatGPTAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CHAT_GPT_API_KEY&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"鸡你太美" refers to which male artist in mainland China? Here&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s a hint: he likes to sing, dance, play basketball, and rap.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;API Key is passed through the environment variable &lt;code&gt;CHAT_GPT_API_KEY&lt;/code&gt;, so we also need to create an environment variable. Click the settings icon in the lower left corner:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5mg-_oKB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-11-18-38-ydsD8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5mg-_oKB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-11-18-38-ydsD8h.png" alt="img" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select "Environment Variable" --&amp;gt; "Add Environment Variables" in turn, enter the name and value of the environment variable, and click "OK", then click "Update", and the application will be restarted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--atgPxceg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-57-SsikP3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--atgPxceg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-18-57-SsikP3.png" alt="" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click "Run" in the upper right corner to debug and run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L0yTAMSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-00-7linkX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L0yTAMSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-00-7linkX.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! Now let's try adding the &lt;strong&gt;context tracking&lt;/strong&gt; feature. It's actually quite simple, just pass in the ID of the previous message during the conversation. Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lafjs/cloud&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatGPTAPI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chatgpt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ChatGPTAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CHAT_GPT_API_KEY&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"鸡你太美" refers to which male artist in mainland China? Here&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s a hint: he likes to sing, dance, play basketball, and rap.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Pass in parentMessageId to track context&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No, his surname is Cai. Please answer again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;parentMessageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run it and see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WxpneN7X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-03-1vAZA8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WxpneN7X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-03-1vAZA8.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Amazing! They got the answer right on the second try!&lt;/p&gt;

&lt;p&gt;Okay, let's start the timer now, as the tutorial is finished and we are ready to implement our ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud Function
&lt;/h2&gt;

&lt;p&gt;Now we can start building our own ChatGPT. First, replace the function from the previous section with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lafjs/cloud&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatGPTAPI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chatgpt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;

  &lt;span class="c1"&gt;// Put the api object into cloud.shared so that we can track context&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ChatGPTAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CHAT_GPT_API_KEY&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;
  &lt;span class="c1"&gt;// If parentMessageId is passed in from the front-end, it means we need to track context&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentMessageId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parentMessageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentMessageId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it should be easy to understand this function, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Front-end
&lt;/h2&gt;

&lt;p&gt;We need a front-end page for our Web version of ChatGPT. First, we need to install the Laf SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;laf-client-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to create a &lt;code&gt;cloud&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Cloud&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;laf-client-sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="c1"&gt;// Create a cloud object, replace &amp;lt;appid&amp;gt; with your own App ID&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Cloud&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://&amp;lt;appid&amp;gt;.laf.dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;getAccessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// No need for authorization here, leave it blank for now&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's take a look at the core code of the front-end, which is very simple, just pass the question and context ID to the cloud function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// The question we are asking&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Same logic as the cloud function, pass in context ID if there is one&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;parentMessageId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parentMessageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parentMessageId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// The answer to our question is in res.text&lt;/span&gt;

  &lt;span class="c1"&gt;// This is the context ID&lt;/span&gt;
  &lt;span class="nx"&gt;parentMessageId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a &lt;strong&gt;little&lt;/strong&gt; bit of additional detail, we can make it look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qyu3fPEy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-06-SYTCr2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qyu3fPEy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-06-SYTCr2.png" alt="" width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding these details, the basic development work is done. Now it's time to deploy the project and share it with your friends, and maybe have a drink or two.&lt;/p&gt;

&lt;p&gt;Speaking of deployment, we should now go buy a server, install Nginx, configure Nginx, set up domain name resolution, bind the domain name...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NO NO NO, I don't allow you to waste your young and beautiful life. Life is short, you need Laf. 😃&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;Open your Laf and navigate to the storage interface. Click on the "+" sign above and create a storage bucket with read-only permissions (you can choose any name).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fs7GdaBH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-20-xs8Usu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fs7GdaBH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-20-xs8Usu.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After creating the bucket, run the build command in your front-end project (I used &lt;code&gt;npm run build&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Once the build is complete, locate the &lt;code&gt;dist&lt;/code&gt; folder and upload everything inside it to the storage bucket you just created, just as I did. Remember to upload everything as is, files are files, and folders are folders.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K9vuO3NF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-21-UmfB9S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K9vuO3NF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-21-UmfB9S.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the upload is complete, you will notice an option in the top right corner to "Enable website hosting". Click on it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ve-_quii--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-23-1VWBAp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ve-_quii--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-23-1VWBAp.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking, a link will appear. Click on it to see what it is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9K7CpxYV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-24-X1U4ts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9K7CpxYV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-24-X1U4ts.png" alt="" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh my goodness! Isn't this the project I just developed?!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4t-i23q_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-25-MlxmPl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4t-i23q_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jsdelivr.icloudnative.io/gh/yangchuansheng/imghosting5%40main/uPic/2023-03-27-19-25-MlxmPl.png" alt="" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! Your project is now online. Share it with your friends!&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Project source code: &lt;a href="https://github.com/zuoFeng59556/chatGPT"&gt;https://github.com/zuoFeng59556/chatGPT&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Example website: &lt;a href="https://lafai.io/"&gt;https://lafai.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>chatgpt</category>
      <category>serverless</category>
      <category>faas</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
