<?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: Stanley</title>
    <description>The latest articles on DEV Community by Stanley (@stan-breaks).</description>
    <link>https://dev.to/stan-breaks</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%2F3278495%2F020a8f4b-b704-4327-b5b6-814cb81c001a.gif</url>
      <title>DEV Community: Stanley</title>
      <link>https://dev.to/stan-breaks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stan-breaks"/>
    <language>en</language>
    <item>
      <title>Migrating from Nginx to Angie: A Real-World Journey from Certbot to Built-in ACME</title>
      <dc:creator>Stanley</dc:creator>
      <pubDate>Sun, 01 Feb 2026 12:31:42 +0000</pubDate>
      <link>https://dev.to/stan-breaks/migrating-from-nginx-to-angie-a-real-world-journey-from-certbot-to-built-in-acme-7a3</link>
      <guid>https://dev.to/stan-breaks/migrating-from-nginx-to-angie-a-real-world-journey-from-certbot-to-built-in-acme-7a3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When it comes to web servers, nginx has been the gold standard for years. However, the emergence of Angie—a modern fork of nginx developed by some of the original nginx team members—offers compelling reasons to consider migration. This article chronicles a real-world migration from nginx with certbot to Angie with its built-in ACME module, including the challenges faced and solutions implemented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Migrate to Angie?
&lt;/h2&gt;

&lt;p&gt;Angie brings several improvements over nginx:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Built-in ACME support&lt;/strong&gt;: No more external certificate management tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced features&lt;/strong&gt;: Advanced load balancing, improved monitoring, and better HTTP/3 support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active development&lt;/strong&gt;: Regular updates and new features from the team that helped create nginx&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backward compatibility&lt;/strong&gt;: Most nginx configurations work with minimal changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Migration Challenge
&lt;/h2&gt;

&lt;p&gt;Our production environment was running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web Server&lt;/strong&gt;: nginx &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Management&lt;/strong&gt;: certbot for Let's Encrypt certificates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application&lt;/strong&gt;: Next.js application proxied through nginx&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domains&lt;/strong&gt;: Multiple domains (tangaacademie.com, api.tangaacademie.com)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to migrate to Angie while replacing certbot with Angie's native ACME module for automated certificate management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Understanding Angie's ACME Module
&lt;/h2&gt;

&lt;p&gt;Unlike certbot, which is an external tool that modifies your web server configuration and manages certificates separately, Angie's ACME module is built directly into the web server. This means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No external processes&lt;/strong&gt;: Everything happens within Angie&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration-based&lt;/strong&gt;: Certificates are managed through Angie's config files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variable-based access&lt;/strong&gt;: Certificates are exposed as embedded variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic renewal&lt;/strong&gt;: Handled internally without cron jobs&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Key Concepts
&lt;/h3&gt;

&lt;p&gt;The ACME module works through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ACME clients&lt;/strong&gt;: Defined in the &lt;code&gt;http&lt;/code&gt; block with unique names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server references&lt;/strong&gt;: Individual servers reference the ACME client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certificate variables&lt;/strong&gt;: &lt;code&gt;$acme_cert_&amp;lt;name&amp;gt;&lt;/code&gt; and &lt;code&gt;$acme_cert_key_&amp;lt;name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic validation&lt;/strong&gt;: Built-in handlers for HTTP, DNS, and ALPN challenges&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Installing Angie
&lt;/h2&gt;

&lt;p&gt;First, we installed Angie following the official documentation:&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;# For RHEL/CentOS/AlmaLinux&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;angie

&lt;span class="c"&gt;# Start and enable Angie&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start angie
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;angie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Initial Configuration Migration
&lt;/h2&gt;

&lt;p&gt;Our original nginx configuration looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Original nginx config with certbot&lt;/span&gt;
&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;tangaacademie.com&lt;/span&gt; &lt;span class="s"&gt;www.tangaacademie.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;tangaacademie.com&lt;/span&gt; &lt;span class="s"&gt;www.tangaacademie.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/tangaacademie.com/fullchain.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/tangaacademie.com/privkey.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;'upgrade'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&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;We migrated this to Angie with ACME:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /etc/angie/angie.conf or /etc/angie/sites-enabled/tangaacademie.conf&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Critical: DNS resolver for ACME&lt;/span&gt;
    &lt;span class="kn"&gt;resolver&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.8.8&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.4.4&lt;/span&gt; &lt;span class="s"&gt;valid=300s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;resolver_timeout&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Define ACME client&lt;/span&gt;
    &lt;span class="kn"&gt;acme_client&lt;/span&gt; &lt;span class="s"&gt;tangaacademie&lt;/span&gt; &lt;span class="s"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;tangaacademie.com&lt;/span&gt; &lt;span class="s"&gt;www.tangaacademie.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;tangaacademie.com&lt;/span&gt; &lt;span class="s"&gt;www.tangaacademie.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Reference ACME client&lt;/span&gt;
        &lt;span class="kn"&gt;acme&lt;/span&gt; &lt;span class="s"&gt;tangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Use ACME-managed certificates via variables&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_tangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_key_tangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;'upgrade'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&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;h2&gt;
  
  
  Step 4: The DNS Resolver Challenge
&lt;/h2&gt;

&lt;p&gt;After initial configuration, we encountered our first major issue. The Angie error logs showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[error] acme-v02.api.letsencrypt.org could not be resolved (5: Operation refused)
[error] request to ACME server failed: -1, ACME client: tangaacademie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;We initially configured the resolver as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;resolver&lt;/span&gt; &lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="s"&gt;.0.53&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a common systemd-resolved stub resolver on modern Linux systems. However, testing revealed the issue:&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;dig @127.0.0.53 acme-v02.api.letsencrypt.org
&lt;span class="p"&gt;;;&lt;/span&gt; flags: qr rd&lt;span class="p"&gt;;&lt;/span&gt; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
&lt;span class="p"&gt;;;&lt;/span&gt; WARNING: recursion requested but not available
status: REFUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local resolver was refusing recursive queries, which the ACME module requires to contact Let's Encrypt servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;We switched to public DNS resolvers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Google DNS&lt;/span&gt;
&lt;span class="k"&gt;resolver&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.8.8&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.4.4&lt;/span&gt; &lt;span class="s"&gt;valid=300s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;resolver_timeout&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;# Alternative: Cloudflare DNS&lt;/span&gt;
&lt;span class="c1"&gt;# resolver 1.1.1.1 1.0.0.1 valid=300s;&lt;/span&gt;
&lt;span class="c1"&gt;# resolver_timeout 5s;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this change, the ACME module successfully connected to Let's Encrypt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Verification and Testing
&lt;/h2&gt;

&lt;p&gt;After applying the configuration, we verified the setup:&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;# Test configuration&lt;/span&gt;
angie &lt;span class="nt"&gt;-t&lt;/span&gt;

&lt;span class="c"&gt;# Reload Angie&lt;/span&gt;
systemctl reload angie

&lt;span class="c"&gt;# Watch for ACME activity&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/angie/error.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logs showed successful certificate acquisition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[notice] creating implicit client block for "@acme"
[notice] ACME: requesting certificate for tangaacademie.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We verified the certificates were issued:&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;openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; /var/lib/angie/acme/tangaacademie/certificate.pem &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-issuer&lt;/span&gt; &lt;span class="nt"&gt;-subject&lt;/span&gt; &lt;span class="nt"&gt;-dates&lt;/span&gt;
&lt;span class="nv"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;US, &lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Let&lt;span class="s1"&gt;'s Encrypt, CN=E7
subject=CN=tangaacademie.com
notBefore=Feb  1 11:04:38 2026 GMT
notAfter=May  2 11:04:37 2026 GMT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect! Let's Encrypt had issued valid certificates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Managing Multiple Domains
&lt;/h2&gt;

&lt;p&gt;For our API subdomain, we created a separate ACME client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;resolver&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.8.8&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.4.4&lt;/span&gt; &lt;span class="s"&gt;valid=300s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;resolver_timeout&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Main domain ACME client&lt;/span&gt;
    &lt;span class="kn"&gt;acme_client&lt;/span&gt; &lt;span class="s"&gt;tangaacademie&lt;/span&gt; &lt;span class="s"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# API subdomain ACME client&lt;/span&gt;
    &lt;span class="kn"&gt;acme_client&lt;/span&gt; &lt;span class="s"&gt;apitangaacademie&lt;/span&gt; &lt;span class="s"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Main domain&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;tangaacademie.com&lt;/span&gt; &lt;span class="s"&gt;www.tangaacademie.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;acme&lt;/span&gt; &lt;span class="s"&gt;tangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_tangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_key_tangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# ... location blocks&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# API subdomain&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;api.tangaacademie.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;acme&lt;/span&gt; &lt;span class="s"&gt;apitangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_apitangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_key_apitangaacademie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# ... location blocks&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;h2&gt;
  
  
  Step 7: Cleaning Up Certbot
&lt;/h2&gt;

&lt;p&gt;After confirming Angie's ACME module was working correctly, we removed certbot:&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;# Stop certbot renewal timer&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop certbot.timer
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl disable certbot.timer

&lt;span class="c"&gt;# Remove certbot&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf remove certbot  &lt;span class="c"&gt;# or apt remove certbot&lt;/span&gt;

&lt;span class="c"&gt;# Clean up old certificates (optional, after backup)&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /etc/letsencrypt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Differences: Certbot vs. Angie ACME
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Certbot&lt;/th&gt;
&lt;th&gt;Angie ACME&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External tool&lt;/td&gt;
&lt;td&gt;Built into web server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modifies server config&lt;/td&gt;
&lt;td&gt;Pure config-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Certificate Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;File paths&lt;/td&gt;
&lt;td&gt;Embedded variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Renewal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cron job required&lt;/td&gt;
&lt;td&gt;Automatic internal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Process&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separate process&lt;/td&gt;
&lt;td&gt;Same process as web server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Lower (once configured)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Startup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web server independent&lt;/td&gt;
&lt;td&gt;Certificates ready at startup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Common Pitfalls and Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. DNS Resolver Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: &lt;code&gt;acme-v02.api.letsencrypt.org could not be resolved&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Use public DNS resolvers instead of local stub resolvers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;resolver&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.8.8&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="s"&gt;.4.4&lt;/span&gt; &lt;span class="s"&gt;valid=300s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Old Certificate Paths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: &lt;code&gt;cannot load certificate "/etc/angie/"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Ensure all &lt;code&gt;ssl_certificate&lt;/code&gt; directives use ACME variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;ssl_certificate&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;client_name&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_key_&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;client_name&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Port 80 Not Open
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: HTTP validation fails&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Ensure port 80 is accessible for ACME HTTP challenges. Angie automatically handles &lt;code&gt;/.well-known/acme-challenge/&lt;/code&gt; requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Missing Resolver Directive
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: ACME client can't contact Let's Encrypt&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Always include a &lt;code&gt;resolver&lt;/code&gt; directive in the &lt;code&gt;http&lt;/code&gt; block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Configuration: DNS Validation
&lt;/h2&gt;

&lt;p&gt;For wildcard certificates or when port 80 isn't available, DNS validation is an option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;acme_client&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt; &lt;span class="s"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;
    &lt;span class="s"&gt;challenge=dns&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt; &lt;span class="s"&gt;*.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;acme&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_example&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="nv"&gt;$acme_cert_key_example&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;This requires configuring your DNS to delegate &lt;code&gt;_acme-challenge.&lt;/code&gt; subdomains to your Angie server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Maintenance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Certificate Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View certificate details&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; /var/lib/angie/acme/&amp;lt;client_name&amp;gt;/certificate.pem &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-text&lt;/span&gt;

&lt;span class="c"&gt;# Check expiration&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; /var/lib/angie/acme/&amp;lt;client_name&amp;gt;/certificate.pem &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-dates&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor ACME Activity
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Watch error log for ACME events&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/angie/error.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; acme

&lt;span class="c"&gt;# Check certificate directory&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/lib/angie/acme/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Debug Mode
&lt;/h3&gt;

&lt;p&gt;For troubleshooting, enable debug logging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;error_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/angie/error.log&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Angie's built-in ACME module offers several performance advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reduced overhead&lt;/strong&gt;: No external processes or file monitoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster startup&lt;/strong&gt;: Certificates are loaded at startup, no waiting for certbot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less disk I/O&lt;/strong&gt;: Direct memory access to certificates via variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simpler architecture&lt;/strong&gt;: Fewer moving parts means fewer failure points&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use strong SSL configuration&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;ssl_protocols&lt;/span&gt; &lt;span class="s"&gt;TLSv1.2&lt;/span&gt; &lt;span class="s"&gt;TLSv1.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ssl_ciphers&lt;/span&gt; &lt;span class="s"&gt;HIGH:!aNULL:!MD5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable OCSP stapling&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;ssl_stapling&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ssl_stapling_verify&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Implement HSTS&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class="s"&gt;"max-age=31536000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;includeSubDomains"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Migrating from nginx with certbot to Angie with built-in ACME proved to be a worthwhile endeavor. The key lessons learned:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DNS resolver configuration is critical&lt;/strong&gt; - Don't rely on local stub resolvers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACME variables simplify configuration&lt;/strong&gt; - No more managing file paths&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in integration is elegant&lt;/strong&gt; - Fewer external dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test thoroughly&lt;/strong&gt; - Verify each domain and subdomain independently&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The migration resulted in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Simplified infrastructure (no more certbot)&lt;/li&gt;
&lt;li&gt;✅ More reliable certificate renewal&lt;/li&gt;
&lt;li&gt;✅ Cleaner configuration&lt;/li&gt;
&lt;li&gt;✅ Improved monitoring capabilities&lt;/li&gt;
&lt;li&gt;✅ Better performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For organizations running nginx, Angie represents a compelling upgrade path that maintains compatibility while offering modern features and simplified certificate management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://angie.software/en/" rel="noopener noreferrer"&gt;Angie Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.angie.software/angie/docs/configuration/acme/" rel="noopener noreferrer"&gt;Angie ACME Configuration Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://letsencrypt.org/docs/" rel="noopener noreferrer"&gt;Let's Encrypt Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc8555" rel="noopener noreferrer"&gt;ACME Protocol Specification (RFC 8555)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;About the Author: This migration was performed on a production environment running Next.js applications with multiple domains in February 2026.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>ytsurf: Watch YouTube Videos Without Leaving Your Terminal</title>
      <dc:creator>Stanley</dc:creator>
      <pubDate>Fri, 20 Jun 2025 05:15:29 +0000</pubDate>
      <link>https://dev.to/stan-breaks/ytsurf-watch-youtube-videos-without-leaving-your-terminal-4e0j</link>
      <guid>https://dev.to/stan-breaks/ytsurf-watch-youtube-videos-without-leaving-your-terminal-4e0j</guid>
      <description>&lt;p&gt;Ever found yourself deep in a coding session, needing to quickly check a tutorial or listen to some background music, but dreading the context switch to your browser? I built &lt;strong&gt;ytsurf&lt;/strong&gt; to solve exactly this problem.&lt;/p&gt;

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

&lt;p&gt;ytsurf is a lightweight shell script that brings YouTube search and playback directly to your terminal. No more alt-tabbing to your browser, getting distracted by recommendations, or losing your flow state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lightning-fast search&lt;/strong&gt;: Search YouTube directly from your command line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive selection&lt;/strong&gt;: Beautiful fzf interface with thumbnail previews via chafa&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart caching&lt;/strong&gt;: Results cached for 10 minutes to speed up repeated searches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero friction playback&lt;/strong&gt;: Selected videos play instantly in mpv&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distraction-free&lt;/strong&gt;: No YouTube UI, no recommendations, just the content you want&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The magic happens through a combination of proven Unix tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;yt-dlp&lt;/code&gt; fetches video information and metadata&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jq&lt;/code&gt; parses the JSON responses&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fzf&lt;/code&gt; provides the interactive selection interface&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chafa&lt;/code&gt; displays thumbnail previews right in your terminal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mpv&lt;/code&gt; handles the video playback&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Arch Linux (Easy Mode)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; ytsurf
&lt;span class="c"&gt;# or&lt;/span&gt;
paru &lt;span class="nt"&gt;-S&lt;/span&gt; ytsurf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manual Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.local/bin
curl &lt;span class="nt"&gt;-o&lt;/span&gt; ~/.local/bin/ytsurf https://raw.githubusercontent.com/Stan-breaks/ytsurf/main/ytsurf.sh
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.local/bin/ytsurf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;It's as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ytsurf &lt;span class="s2"&gt;"javascript async await tutorial"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run it without arguments for interactive mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ytsurf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get a beautiful fzf interface showing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Video titles (truncated for readability)&lt;/li&gt;
&lt;li&gt;Duration&lt;/li&gt;
&lt;li&gt;Channel name&lt;/li&gt;
&lt;li&gt;Live thumbnail previews as you navigate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Press Enter to start watching with mpv!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;As developers, we live in the terminal. Whether it's running builds, managing git repos, or debugging issues, the command line is our home. But every time we needed to reference a video tutorial or wanted some background music, we had to break our flow by switching to a browser.&lt;/p&gt;

&lt;p&gt;YouTube's web interface, while great for casual browsing, can be a productivity killer with its recommendations and rabbit holes. ytsurf gives you the content you need without the distractions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Details
&lt;/h2&gt;

&lt;p&gt;The script leverages &lt;code&gt;yt-dlp&lt;/code&gt;'s search capabilities to fetch the top 10 results for any query. It then uses &lt;code&gt;jq&lt;/code&gt; to parse and format the JSON response into a readable list. The thumbnail URLs are passed to &lt;code&gt;chafa&lt;/code&gt; for in-terminal image display, creating a surprisingly rich browsing experience.&lt;/p&gt;

&lt;p&gt;Caching is implemented using simple file timestamps - if a search was performed within the last 10 minutes, it uses the cached results instead of making a new API call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance &amp;amp; Efficiency
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cached searches&lt;/strong&gt;: Sub-second response times for repeated queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt;: Uses existing system tools, no heavy dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bandwidth efficient&lt;/strong&gt;: Only downloads thumbnails for viewed results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory friendly&lt;/strong&gt;: Minimal resource usage compared to browser tabs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;p&gt;I'm considering adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Playlist support&lt;/li&gt;
&lt;li&gt;History tracking&lt;/li&gt;
&lt;li&gt;Custom mpv options&lt;/li&gt;
&lt;li&gt;Integration with other video platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Out!
&lt;/h2&gt;

&lt;p&gt;If you're a terminal enthusiast who values productivity and minimal distractions, give ytsurf a try. It's open source, lightweight, and designed by developers, for developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Stan-breaks/ytsurf" rel="noopener noreferrer"&gt;Stan-breaks/ytsurf&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;AUR Package&lt;/strong&gt;: &lt;code&gt;ytsurf&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;What terminal tools have transformed your workflow? Drop a comment below - I'd love to hear about your favorite productivity hacks!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>opensource</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
