<?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>Forem: Sergio Peris</title>
    <description>The latest articles on Forem by Sergio Peris (@sertxudev).</description>
    <link>https://forem.com/sertxudev</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%2F253472%2Ff38f54c9-d1ce-49a0-bd76-73669ef92cc4.jpg</url>
      <title>Forem: Sergio Peris</title>
      <link>https://forem.com/sertxudev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sertxudev"/>
    <language>en</language>
    <item>
      <title>Deploying a FastAPI app to Kubernetes with health probes</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 04 May 2026 09:00:24 +0000</pubDate>
      <link>https://forem.com/sertxudev/deploying-a-fastapi-app-to-kubernetes-with-health-probes-fm5</link>
      <guid>https://forem.com/sertxudev/deploying-a-fastapi-app-to-kubernetes-with-health-probes-fm5</guid>
      <description>&lt;p&gt;This week, I was updating the image of a FastAPI app in our Kubernetes cluster, but I took the whole app down because the process failed due to an incompatible dependency with our server. The updated pod was unable to start, but we didn't have health checks in place, so the deployment continued to update the other replicas, taking down all app instances.&lt;/p&gt;

&lt;p&gt;In this tutorial, I will explain how to add a health check to your FastAPI app to ensure the deployment doesn't continue updating the pods if they fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a health endpoint to your FastAPI app
&lt;/h2&gt;

&lt;p&gt;Kubernetes requires two endpoints, liveness and readiness. What's the difference between them?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Liveness endpoint&lt;/strong&gt;: Should return a &lt;code&gt;200 OK&lt;/code&gt; as long as the app is running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readiness endpoint&lt;/strong&gt;: Should return a &lt;code&gt;200 OK&lt;/code&gt; when the app is ready to handle traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your app takes some time from boot since it's ready to start handling traffic, it makes sense to have two different endpoints. This also prevents Kubernetes from killing the updated pod while it's still booting.&lt;/p&gt;

&lt;p&gt;For this tutorial, we will create only one endpoint at &lt;code&gt;/health&lt;/code&gt;. This is a minimal FastAPI app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Containerizing the app
&lt;/h2&gt;

&lt;p&gt;To run this app in Kubernetes, you should create a Docker image. This is a basic image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.13-slim&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; main.py .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "main:app", "--log-level", "warning", "--host", "0.0.0.0", "--port", "8000"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;requirements.txt&lt;/code&gt; file only needs &lt;code&gt;fastapi&lt;/code&gt; and &lt;code&gt;uvicorn&lt;/code&gt; for this example. &lt;/p&gt;

&lt;p&gt;Then we build the Docker image, and push it to a registry your Kubernetes cluster can access, like Docker Hub or a private registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; fastapi-health-app:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Kubernetes deployment manifest
&lt;/h2&gt;

&lt;p&gt;To deploy the app in your Kubernetes cluster, you need to create a deployment manifest.&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-app&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&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;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-app&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-app&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-app&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-health-app:latest&lt;/span&gt;
        &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;

        &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;timeoutSeconds&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;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

        &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
          &lt;span class="na"&gt;timeoutSeconds&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;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this configuration, both liveness and rediness probes use &lt;code&gt;HTTP GET&lt;/code&gt; requests to the path &lt;code&gt;/health&lt;/code&gt; on port &lt;code&gt;8000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;initialDelaySeconds&lt;/code&gt; option gives the app time to start before the first health check.&lt;/p&gt;

&lt;p&gt;For liveness, the &lt;code&gt;periodSeconds&lt;/code&gt; option sets the check interval to 10 seconds.&lt;br&gt;
The &lt;code&gt;failureThreshold&lt;/code&gt; option will restart the pod after 3 consecutive failed checks.&lt;/p&gt;

&lt;p&gt;For readiness, the &lt;code&gt;periodSeconds&lt;/code&gt; option sets the check interval to 5 seconds.&lt;br&gt;
The &lt;code&gt;failureThreshold&lt;/code&gt; option will mark the pod as not ready and remove it from service load balancers after one failed check.&lt;/p&gt;

&lt;p&gt;To expose the app you need to create a service. Here's a simple service manifest:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-app-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-app&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Applying the configuration
&lt;/h2&gt;

&lt;p&gt;Once you've created both manifests, you should deploy them to your cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a few seconds for the pods to start, then you can check the status with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                 READY   STATUS    RESTARTS   AGE
fastapi-app-86b64cbbd5-9qkvd         1/1     Running   0          5m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the pod in a &lt;code&gt;Running&lt;/code&gt; state with &lt;code&gt;READY 1/1&lt;/code&gt; once the readiness probe passes, meaning your app is ready to start receiving requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying the probes work
&lt;/h2&gt;

&lt;p&gt;To confirm the probes are working, you can check the pod details with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pod &lt;span class="o"&gt;[&lt;/span&gt;pod-name]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for the Liveness and Readiness sections in the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
Liveness:  http-get http://:8000/health delay=5s timeout=1s period=10s #success=1 #failure=3
Readiness: http-get http://:8000/health delay=5s timeout=1s period=5s #success=1 #failure=1
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you port forward the service you can also test the endpoint locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/fastapi-app-service 8000:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use curl to see the endpoint response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl localhost:8000/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In closing
&lt;/h2&gt;

&lt;p&gt;Adding health endpoints to a FastAPI app takes only a few lines of code, and configuring Kubernetes probes is fairly easy. &lt;/p&gt;

&lt;p&gt;The key difference is tunning the liveness and readiness probe options to your app needs.&lt;/p&gt;

&lt;p&gt;If you want to extend this setup, you could add a different endpoint for readiness probe that verifies a database connection or a cache is available before marking the app as ready.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>kubernetes</category>
      <category>python</category>
    </item>
    <item>
      <title>Switching a Zebra printer to Thermal Transfer Mode with ZPL</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:00:23 +0000</pubDate>
      <link>https://forem.com/sertxudev/switching-a-zebra-printer-to-thermal-transfer-mode-with-zpl-5bjl</link>
      <guid>https://forem.com/sertxudev/switching-a-zebra-printer-to-thermal-transfer-mode-with-zpl-5bjl</guid>
      <description>&lt;p&gt;Zebra printers can be configured using ZPL commands; one of the main configurations is the Media Type, as printers can be either Thermal transfer or Direct thermal.&lt;/p&gt;

&lt;p&gt;Thermal Transfer printers require a ribbon to print, while Direct Thermal printers require thermal labels.&lt;/p&gt;

&lt;p&gt;Zebra printers use the &lt;code&gt;^MTa&lt;/code&gt; command to set the print method. We have the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;^MTT&lt;/code&gt;: Thermal Transfer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;^MTD&lt;/code&gt;: Direct Thermal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To change the printer settings to Thermal Transfer, we must send the following ZPL commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;^XA
^MTT
^XZ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will change the media type of our printer for the current job.&lt;/p&gt;

&lt;p&gt;If we want to persist this change between boots, we must save the settings using the &lt;code&gt;^JUS&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;So, the complete configuration will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;^XA
^MTT
^JUS
^XZ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>zpl</category>
    </item>
    <item>
      <title>Configure static IP address on Netplan</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Tue, 31 Mar 2026 09:00:23 +0000</pubDate>
      <link>https://forem.com/sertxudev/configure-static-ip-address-on-netplan-2am1</link>
      <guid>https://forem.com/sertxudev/configure-static-ip-address-on-netplan-2am1</guid>
      <description>&lt;p&gt;I've recently set up a new Ubuntu Server 24.04 and needed to assign it a static IP address. For a server, you don't want the IP to change whenever DHCP leases expire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding your network interface
&lt;/h2&gt;

&lt;p&gt;Before making any configuration changes, you need to know the name of your network interface. On Ubuntu Server, you'll likely see something like &lt;code&gt;ens33&lt;/code&gt; or &lt;code&gt;enp0s3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run this command to see all network interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip &lt;span class="nb"&gt;link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for the interface that has an IP address assigned to it. That's the one you want to configure. In my case, it was &lt;code&gt;enp0s3&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Netplan
&lt;/h2&gt;

&lt;p&gt;Ubuntu Server 24.04 stores network configuration in &lt;code&gt;/etc/netplan/&lt;/code&gt;. There's usually a file there called something like &lt;code&gt;50-cloud-init.yaml&lt;/code&gt;. Let's check what's in that 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="nb"&gt;ls&lt;/span&gt; /etc/netplan/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the configuration file and you will see something like this by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /etc/netplan/50-cloud-init.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;renderer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networkd&lt;/span&gt;
  &lt;span class="na"&gt;ethernets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enp0s3&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;dhcp4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change it to use a static IP instead. Here's what I configured:&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;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;renderer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networkd&lt;/span&gt;
  &lt;span class="na"&gt;ethernets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enp0s3&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;dhcp4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;dhcp6&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.1.100/24&lt;/span&gt;
      &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
          &lt;span class="na"&gt;via&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.1.1&lt;/span&gt;
      &lt;span class="na"&gt;nameservers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;1.1.1.1&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;1.0.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the IP address includes the CIDR notation. The CIDR &lt;code&gt;/24&lt;/code&gt; means a subnet mask of &lt;code&gt;255.255.255.0&lt;/code&gt;. I'm using explicit routes instead of the older &lt;code&gt;gateway4&lt;/code&gt; key, because Ubuntu 24.04 deprecated &lt;code&gt;gateway4&lt;/code&gt;. For DNS, I pointed to Cloudflare's public DNS servers, but you can use the DNS servers of your choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying the changes
&lt;/h2&gt;

&lt;p&gt;Once the file is saved, instruct Netplan to apply the changes:&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="nb"&gt;sudo &lt;/span&gt;netplan apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verifying the changes
&lt;/h2&gt;

&lt;p&gt;Check that your static IP is now assigned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip addr show enp0s3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should now show your new static IP address.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>networking</category>
      <category>ubuntu</category>
      <category>linux</category>
    </item>
    <item>
      <title>Backup S3 buckets using rclone</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Tue, 10 Feb 2026 08:00:24 +0000</pubDate>
      <link>https://forem.com/sertxudev/backup-s3-buckets-using-rclone-e6p</link>
      <guid>https://forem.com/sertxudev/backup-s3-buckets-using-rclone-e6p</guid>
      <description>&lt;p&gt;Backing up our files is a crucial task that we don't consider until it's too late. Using &lt;code&gt;rclone&lt;/code&gt;, we can make a backup of all our data stored in S3 buckets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing rclone
&lt;/h2&gt;

&lt;p&gt;First, we need to install &lt;code&gt;rclone&lt;/code&gt;. We'll be using Ubuntu Server, but you can use any distribution supported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://rclone.org/install.sh | &lt;span class="nb"&gt;sudo &lt;/span&gt;bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we've installed it, we can verify it.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rclone v1.72.1
 - os/version: ubuntu 24.04 (64 bit)
 - os/kernel: 6.8.0-90-generic (x86_64)
 - os/type: linux
 - os/arch: amd64
 - go/version: go1.25.5
 - go/linking: static
 - go/tags: none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure the remote
&lt;/h2&gt;

&lt;p&gt;To connect to our S3 storage, we must create a rclone remote.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This command will prompt us to answer a few questions to configure it correctly.&lt;/p&gt;

&lt;p&gt;Some options may differ from this example, so take your time reviewing the options offered to select the one that best suits your case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n) New remote
name&amp;gt; s3-storage

Storage&amp;gt; s3
provider&amp;gt; Other
env_auth&amp;gt; false
access_key_id&amp;gt; ACCESS_ID_KEY
secret_access_key&amp;gt; ACCESS_SECRET_KEY
region&amp;gt; REGION
endpoint&amp;gt; http://s3.example.com
location_constraint&amp;gt;
acl&amp;gt; private
y/e/d&amp;gt; y
e/n/d/r/c/s/q&amp;gt; q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test the remote
&lt;/h2&gt;

&lt;p&gt;Once we've configured the remote, we need to test it. Running the following command should list all buckets available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rclone lsd s3-storage:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-1 2026-01-29 21:09:35        -1 bucket-1
-1 2026-01-29 20:05:31        -1 bucket-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring the backup
&lt;/h2&gt;

&lt;p&gt;To back up the S3 storage, we'll create a backup script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /usr/local/bin/s3-backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/rclone-backup.log"&lt;/span&gt;
&lt;span class="nv"&gt;DEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/backup"&lt;/span&gt;

rclone &lt;span class="nb"&gt;sync &lt;/span&gt;garage: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--checksum&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--fast-list&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--s3-no-check-bucket&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transfers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--checkers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--log-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--log-level&lt;/span&gt; INFO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will back up all S3 buckets to the &lt;code&gt;/backup&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Next, we make the script executable.&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="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/s3-backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run it to create the first backup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;s3-backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up a cron job
&lt;/h2&gt;

&lt;p&gt;To run the backup periodically, we must create a cron job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Backup Garage S3 to QNAP daily at 02:00.
0 2 * * * /usr/local/bin/s3-backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This cron job will run the backup script every day at 02:00.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cli</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Add an NFS mount point to a Ubuntu Server</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 02 Feb 2026 09:00:24 +0000</pubDate>
      <link>https://forem.com/sertxudev/add-an-nfs-mount-point-to-a-ubuntu-server-2k9n</link>
      <guid>https://forem.com/sertxudev/add-an-nfs-mount-point-to-a-ubuntu-server-2k9n</guid>
      <description>&lt;p&gt;NFS allows us to mount a network share as a local device. This system uses the client IP address for authentication. Don't confuse it with SMB, which allows user and password authentication.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll explain how to connect an NFS share to an Ubuntu Server as a client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install dependencies
&lt;/h2&gt;

&lt;p&gt;First, we must install the dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nfs-common
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a mount point
&lt;/h2&gt;

&lt;p&gt;Once we've installed the dependencies, we must ensure the mount point we want to use exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/backups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add a persistent mount
&lt;/h2&gt;

&lt;p&gt;Using the mount command, we can temporarily mount the NFS share. However, we'll modify the &lt;code&gt;/etc/fstab&lt;/code&gt; file to make the mount persistent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.10:/Backups /mnt/backups nfs defaults,_netdev 0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To apply the changes we've made at &lt;code&gt;/etc/fstab&lt;/code&gt; we must run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can check that the NFS mount is working as expected.&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="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Filesystem                     Size  Used Avail Use% Mounted on
/dev/sda2                       30G  6.9G   21G  25% /
192.168.1.10:/Backups          916G  594G  322G  65% /mnt/backups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Remove local and local-lvm from Proxmox</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 26 Jan 2026 09:00:24 +0000</pubDate>
      <link>https://forem.com/sertxudev/remove-local-and-local-lvm-from-proxmox-37i6</link>
      <guid>https://forem.com/sertxudev/remove-local-and-local-lvm-from-proxmox-37i6</guid>
      <description>&lt;p&gt;Proxmox, by default, configures two storage volumes on the disk where the Proxmox OS resides: one named &lt;code&gt;local-lvm&lt;/code&gt; for storing the disk images and containers, and another named &lt;code&gt;local&lt;/code&gt; for storing the ISO images, container templates, and backups.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;local-lvm&lt;/code&gt;, as its name suggests, is a LVM thin-provisioned.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;local&lt;/code&gt; is simply a directory stored at &lt;code&gt;/var/lib/vz&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's common to have more than one disk in a Proxmox host, so the best approach is to separate the Proxmox OS from the disk images. For example, a node with one 480GB disk for the Proxmox OS and two 960GB disks in RAID 0 for storing the disk images and containers.&lt;/p&gt;

&lt;p&gt;To remove the &lt;code&gt;local-lvm&lt;/code&gt; and &lt;code&gt;local&lt;/code&gt; storage volumes, follow the steps below.&lt;/p&gt;

&lt;p&gt;Using the Proxmox GUI, navigate to &lt;strong&gt;Datacenter&lt;/strong&gt; &amp;gt; &lt;strong&gt;Storage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There, select the &lt;code&gt;local-lvm&lt;/code&gt; storage volume and remove it. Next, select the &lt;code&gt;local&lt;/code&gt; storage volume and edit it to disable it, as this storage volume cannot be deleted.&lt;/p&gt;

&lt;p&gt;Now, open a shell on your node, as you will run a few commands.&lt;/p&gt;

&lt;p&gt;First, you should remove the logical volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lvremove /dev/pve/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you're going to reclaim the free space that is left unused in the logical volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lvresize &lt;span class="nt"&gt;-l&lt;/span&gt; +100%FREE /dev/pve/root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you must expand the disk format to be able to use the newly added space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;resize2fs /dev/mapper/pve-root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these steps, you've successfully removed the local storage volumes from your Proxmox.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>proxmox</category>
    </item>
    <item>
      <title>Registry CRUD actions on Windows Group Policy</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Wed, 21 Jan 2026 09:00:24 +0000</pubDate>
      <link>https://forem.com/sertxudev/registry-crud-actions-on-windows-group-policy-3nji</link>
      <guid>https://forem.com/sertxudev/registry-crud-actions-on-windows-group-policy-3nji</guid>
      <description>&lt;p&gt;Using Group Policies, you can modify the registry of all computers joined to the domain.&lt;/p&gt;

&lt;p&gt;When you create a new GPO, one of the parameters you're required to configure is the action that will be performed. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create&lt;/li&gt;
&lt;li&gt;Update&lt;/li&gt;
&lt;li&gt;Replace&lt;/li&gt;
&lt;li&gt;Delete&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But do you know what each action entails?&lt;/p&gt;

&lt;h3&gt;
  
  
  Create action
&lt;/h3&gt;

&lt;p&gt;This action will create the registry key if it doesn't exist. If it already exists, it will do nothing, even if the actual registry value doesn't match the value set in the GPO.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update action
&lt;/h3&gt;

&lt;p&gt;This is the default action. It updates the registry value to the one set in the GPO. If the registry key doesn't exist, it will also be created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replace action
&lt;/h3&gt;

&lt;p&gt;This action will delete the registry key if it already exists and then recreate it. If it doesn't exist, it will be created. This is similar to the update action but more aggressive, because it will delete the registry key every time it is applied. This action is rarely used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete action
&lt;/h3&gt;

&lt;p&gt;This action will delete the registry key if it's present.&lt;/p&gt;



&lt;p&gt;Now that you understand how each action works, you can begin configuring the GPO on your domain controller.&lt;/p&gt;

</description>
      <category>windows</category>
    </item>
    <item>
      <title>Disabling Fast Boot via Windows Registry</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 19 Jan 2026 09:00:23 +0000</pubDate>
      <link>https://forem.com/sertxudev/disabling-fast-boot-via-windows-registry-23hi</link>
      <guid>https://forem.com/sertxudev/disabling-fast-boot-via-windows-registry-23hi</guid>
      <description>&lt;p&gt;Fast Boot allows us to boot our computer faster by performing a deep hibernation instead of completely powering it off.&lt;/p&gt;

&lt;p&gt;If you have startup scripts, you should disable Fast Boot because it will prevent running the scripts, as your computer will not be "starting up".&lt;/p&gt;

&lt;p&gt;In a domain-controlled environment, you can set a Group Policy that disables Fast Boot for all your computers by modifying the Registry.&lt;/p&gt;

&lt;p&gt;You should configure the following Registry key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Power\HiberbootEnable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the value &lt;code&gt;0&lt;/code&gt; as a &lt;code&gt;DWORD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this Registry key, your computer will skip the Fast Boot and perform a complete power-off and startup cycle.&lt;/p&gt;

</description>
      <category>windows</category>
    </item>
    <item>
      <title>Trust any proxy in Laravel</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 12 Jan 2026 09:00:23 +0000</pubDate>
      <link>https://forem.com/sertxudev/trust-any-proxy-in-laravel-501n</link>
      <guid>https://forem.com/sertxudev/trust-any-proxy-in-laravel-501n</guid>
      <description>&lt;p&gt;It's a common practice to deploy Laravel apps behind load balancers, aka proxy.&lt;/p&gt;

&lt;p&gt;But doing this requires you to change the middleware settings so Laravel trusts your load balancer.&lt;/p&gt;

&lt;p&gt;If you don't change it, some side effects may occur, such as hitting rate limiters on for all users at once instead of per IP address.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;bootstrap/app.php&lt;/code&gt; file, you need to add the following inside the &lt;code&gt;-&amp;gt;withMiddleware&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;trustProxies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The asterisk &lt;code&gt;*&lt;/code&gt; will trust any load balancer.&lt;/p&gt;

&lt;p&gt;Please note that it's recommended to narrow down the allowed proxies, because any user can tamper with the header used to determine the real user IP.&lt;/p&gt;

&lt;p&gt;If you only have one load balancer and you know the IP address, you can set the IP address like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;trustProxies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'10.0.0.2'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have multiple load balancers, you can provide an array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;trustProxies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'10.0.0.2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'10.0.1.2'&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;Or even a CIDR for the IP addresses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;trustProxies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'10.0.0.0/8'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In some cases, the load balancer adds its IP address to the right side of the &lt;code&gt;X-Forwarded-For&lt;/code&gt; standard header, maintaining the leftmost IP address as the actual user IP.&lt;/p&gt;

&lt;p&gt;To ensure Laravel obtains the correct IP address for the user's IP, you may need to set it to &lt;code&gt;0.0.0.0/0&lt;/code&gt;, allowing any load balancer to discard all IP addresses in this header except the leftmost one.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Trust proxy with Traefik in K8s</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 05 Jan 2026 09:00:22 +0000</pubDate>
      <link>https://forem.com/sertxudev/trust-proxy-with-traefik-in-k8s-582k</link>
      <guid>https://forem.com/sertxudev/trust-proxy-with-traefik-in-k8s-582k</guid>
      <description>&lt;p&gt;Traefik, by default, will not trust the standard &lt;code&gt;Forwarding&lt;/code&gt; headers Load Balancers usually populate with the client and proxy IPs.&lt;/p&gt;

&lt;p&gt;If your apps don’t need to know the user’s real IP, you don’t need to change any configuration.&lt;br&gt;
However, if you want to use the &lt;code&gt;X-Forwarded-For&lt;/code&gt; header, some changes are required.&lt;/p&gt;

&lt;p&gt;In your Traefik’s yaml configuration, you need to add the following argument with all the IP CIDRs from your Load Balancers. For example, if your Load Balancer is at &lt;code&gt;10.0.0.3&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--entryPoints.web.forwardedHeaders.trustedIPs=10.0.0.3/32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you use MicroK8s, the arguments are in the Traefik daemonset resource.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you don’t know the Load Balancer IPs, you can use &lt;code&gt;0.0.0.0/0&lt;/code&gt; to trust all IPs, but this is not recommended, as any user can fake this header to bypass security features, such as IP-based rate limiters.&lt;/p&gt;

&lt;p&gt;Trusting all IPs at Traefik requires validating the &lt;code&gt;X-Forwarded-For&lt;/code&gt; header inside the application.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>kubernetes</category>
      <category>microk8s</category>
    </item>
    <item>
      <title>MicroK8s upgrade ingress from NGINX to Traefik</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 29 Dec 2025 09:00:25 +0000</pubDate>
      <link>https://forem.com/sertxudev/microk8s-upgrade-ingress-from-nginx-to-traefik-mn8</link>
      <guid>https://forem.com/sertxudev/microk8s-upgrade-ingress-from-nginx-to-traefik-mn8</guid>
      <description>&lt;p&gt;The Kubernetes nginx-ingress is scheduled to be retired in March 2026, so Canonical’s MicroK8s team replaced Nginx with Traefik for its ingress addon.&lt;/p&gt;

&lt;p&gt;If you’re running a MicroK8s cluster with the Nginx ingress, you may want to upgrade it.&lt;/p&gt;

&lt;p&gt;First, you need to disable the ingress addon to remove the old Nginx ingress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s disable ingress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you should update the core addons repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s addons repo update core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can enable the new Traefik ingress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s &lt;span class="nb"&gt;enable &lt;/span&gt;ingress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Traefik ingress is compatible with your previous Nginx configuration, so all your sites and services should work fine.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>linux</category>
      <category>kubernetes</category>
      <category>microk8s</category>
    </item>
    <item>
      <title>Passing Laravel route to Maizzle button</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 08 Dec 2025 09:00:26 +0000</pubDate>
      <link>https://forem.com/sertxudev/passing-laravel-route-to-maizzle-button-57</link>
      <guid>https://forem.com/sertxudev/passing-laravel-route-to-maizzle-button-57</guid>
      <description>&lt;p&gt;Maizzle is a framework that helps us quickly build HTML emails with Tailwind CSS. We can integrate it into our Laravel project to create beautiful emails.&lt;/p&gt;

&lt;p&gt;By default, Maizzle provides us with a button component which can be used as a CTA button in our emails, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;x-button&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://maizzle.com"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-slate-950 hover:bg-slate-800"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Verify email&lt;span class="nt"&gt;&amp;lt;/x-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Laravel, it's common to use the helper &lt;code&gt;route&lt;/code&gt; to create links to different routes of the project.&lt;/p&gt;

&lt;p&gt;For a "Verify email" button like the previous, we might modify it to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;x-button&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"route('auth.verify-email')"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-slate-950 hover:bg-slate-800"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Verify email&lt;span class="nt"&gt;&amp;lt;/x-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we generate the email with this code, Maizzle will output this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"route('dns-check.results', [$check-&amp;gt;project, $check])"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the URL will not work as it was placed without being interpreted.&lt;/p&gt;

&lt;p&gt;We might try to add the curly brackets to allow the &lt;code&gt;route&lt;/code&gt; helper to be interpreted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;x-button&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('auth.verify-email') }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Maizzle will generate this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ route(&amp;amp;amp;#039;auth.verify-email&amp;amp;amp;#039;) }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, that's not working, maybe we need to add &lt;code&gt;@&lt;/code&gt; to escape the content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;x-button&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"@{{ route('auth.verify-email') }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these, Maizzle will generate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ route(&amp;amp;#039;auth.verify-email&amp;amp;#039;]) }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this is not working.&lt;/p&gt;

&lt;p&gt;The solution is to escape the content within the button component, not outside.&lt;/p&gt;

&lt;p&gt;So we will use the button as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;x-button&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"route('auth.verify-email')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;button.html&lt;/code&gt; component should be modified from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;attributes&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{{ href }}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;attributes&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"@{{ {{{ href }}} }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we try it again, Maizzle will generate this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('auth.verify-email') }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when we send the email from our Laravel app, the button will have the correct link.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>tutorial</category>
      <category>maizzle</category>
    </item>
  </channel>
</rss>
