<?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: Sergio Peris</title>
    <description>The latest articles on DEV Community by Sergio Peris (@sertxudev).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F253472%2Ff38f54c9-d1ce-49a0-bd76-73669ef92cc4.jpg</url>
      <title>DEV Community: Sergio Peris</title>
      <link>https://dev.to/sertxudev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sertxudev"/>
    <language>en</language>
    <item>
      <title>How to upgrade a MicroK8s cluster</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Mon, 20 Jul 2026 09:00:22 +0000</pubDate>
      <link>https://dev.to/sertxudev/how-to-upgrade-a-microk8s-cluster-3pib</link>
      <guid>https://dev.to/sertxudev/how-to-upgrade-a-microk8s-cluster-3pib</guid>
      <description>&lt;p&gt;For this tutorial, we'll use a 3-node MicroK8s cluster with nodes k8s-1, k8s-2, and k8s-3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl get nodes
&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    STATUS   ROLES    AGE    VERSION
k8s-1   Ready    &amp;lt;none&amp;gt;   142d   v1.35.6
k8s-2   Ready    &amp;lt;none&amp;gt;   142d   v1.35.6
k8s-3   Ready    &amp;lt;none&amp;gt;   142d   v1.35.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upgrade the first node
&lt;/h2&gt;

&lt;p&gt;To upgrade a node, we must cordon it first so new pods cannot be scheduled on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl cordon k8s-1
&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;node/k8s-1 cordoned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we get the node list again, we can see that it now has pod scheduling disabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl get nodes
&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    STATUS                     ROLES    AGE    VERSION
k8s-1   Ready,SchedulingDisabled   &amp;lt;none&amp;gt;   142d   v1.35.6
k8s-2   Ready                      &amp;lt;none&amp;gt;   142d   v1.35.6
k8s-3   Ready                      &amp;lt;none&amp;gt;   142d   v1.35.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we must remove the remaining pods on this node by draining it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl drain k8s-1 &lt;span class="nt"&gt;--ignore-daemonsets&lt;/span&gt; &lt;span class="nt"&gt;--delete-emptydir-data&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;node/k8s-1 already cordoned
Warning: ignoring DaemonSet-managed Pods: ingress/traefik-zgvnx, kube-system/calico-node-2nlwf
evicting pod sertxudev/sertxudev-worker-86d556b9cd-f5nhw
evicting pod postgresql/postgresql-cluster-3
evicting pod sertxudev/sertxudev-app-7d46bdbbcc-977lf
evicting pod sertxudev/sertxudev-scheduler-5996645758-8fsht
pod/postgresql-cluster-3 evicted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can check that our node no longer has pods, except from the required base ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;--field-selector&lt;/span&gt; spec.nodeName&lt;span class="o"&gt;=&lt;/span&gt;k8s-1
&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;NAMESPACE     NAME                READY   STATUS    RESTARTS   AGE
ingress       traefik-zgvnx       1/1     Running   0          142d
kube-system   calico-node-2nlwf   1/1     Running   0          17d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our node is empty, we can proceed with the upgrade. It's recommended to upgrade one minor version at a time. You can review the latest MicroK8s version on &lt;a href="https://github.com/canonical/microk8s/releases" rel="noopener noreferrer"&gt;GitHub releases&lt;br&gt;
&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ensure you're connected to the node you want to upgrade. Previous commands can be run from any K8s node, but &lt;code&gt;snap refresh&lt;/code&gt; should be run in the node you want to upgrade.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;snap refresh microk8s &lt;span class="nt"&gt;--channel&lt;/span&gt; 1.36/stable
&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;microk8s (1.36/stable) v1.36.2 from Canonical✓ refreshed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the MicroK8s snap has been upgraded, we can let K8s start creating pods in our node by uncordoning it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl uncordon k8s-1
&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;node/k8s-1 uncordoned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we check the cluster nodes again, we can see that our current node has been updated.&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 nodes
&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    STATUS   ROLES    AGE    VERSION
k8s-1   Ready    &amp;lt;none&amp;gt;   142d   v1.36.2
k8s-2   Ready    &amp;lt;none&amp;gt;   142d   v1.35.6
k8s-3   Ready    &amp;lt;none&amp;gt;   142d   v1.35.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the upgraded node is ready to receive pods again, we can begin to upgrade the next node. By running the same commands we've just seen for the next node until all nodes are upgraded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rebalance deployments
&lt;/h2&gt;

&lt;p&gt;Once our cluster is up-to-date, we might want to rebalance our deployments so pods are spread among all nodes.&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 nodes
&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    STATUS   ROLES    AGE    VERSION
k8s-1   Ready    &amp;lt;none&amp;gt;   142d   v1.36.2
k8s-2   Ready    &amp;lt;none&amp;gt;   142d   v1.36.2
k8s-3   Ready    &amp;lt;none&amp;gt;   142d   v1.36.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we should get the deployments that our cluster hosts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;microk8s kubectl get deployments &lt;span class="nt"&gt;-A&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;NAMESPACE      NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
cert-manager   cert-manager              1/1     1            1           441d
cert-manager   cert-manager-cainjector   1/1     1            1           441d
cert-manager   cert-manager-webhook      1/1     1            1           441d
cnpg-system    barman-cloud              1/1     1            1           333d
cnpg-system    cnpg-controller-manager   1/1     1            1           333d
kube-system    calico-kube-controllers   1/1     1            1           454d
kube-system    coredns                   1/1     1            1           454d
kube-system    hostpath-provisioner      1/1     1            1           448d
kube-system    metrics-server            1/1     1            1           444d
sertxudev      sertxudev-app             2/2     2            2           200d
sertxudev      sertxudev-scheduler       1/1     1            1           420d
sertxudev      sertxudev-worker          1/1     1            1           420d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we restart the rollout of each deployment we want to rebalance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout restart deployment &lt;span class="nt"&gt;-n&lt;/span&gt; sertxudev sertxudev-app
kubectl rollout restart deployment &lt;span class="nt"&gt;-n&lt;/span&gt; sertxudev sertxudev-scheduler
kubectl rollout restart deployment &lt;span class="nt"&gt;-n&lt;/span&gt; sertxudev sertxudev-worker
&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;deployment.apps/sertxudev-app restarted
deployment.apps/sertxudev-scheduler restarted
deployment.apps/sertxudev-worker restarted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have an up-to-date MicroK8s cluster ready to continue serving traffic as usual.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>microk8s</category>
    </item>
    <item>
      <title>Sync Windows server time using NTP server</title>
      <dc:creator>Sergio Peris</dc:creator>
      <pubDate>Thu, 02 Jul 2026 09:00:22 +0000</pubDate>
      <link>https://dev.to/sertxudev/sync-windows-server-time-using-ntp-server-3dj5</link>
      <guid>https://dev.to/sertxudev/sync-windows-server-time-using-ntp-server-3dj5</guid>
      <description>&lt;p&gt;We have a few scheduled tasks running on a Windows server and monitored by a self-hosted Sentry instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F823on9d2bxal6az9z8bq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F823on9d2bxal6az9z8bq.png" width="799" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sentry was reporting to us about missed check-ins. Looking at the monitor details, we've noticed the scheduled task was being dispatched early. So the expected dispatch at the start of every minute (&lt;code&gt;0 * * * *&lt;/code&gt;) was never fulfilled.&lt;/p&gt;

&lt;p&gt;Visiting the website &lt;a href="https://time.is" rel="noopener noreferrer"&gt;time.is&lt;/a&gt; from the Windows server we’ve identified that our clock was 5 seconds ahead.&lt;/p&gt;

&lt;p&gt;This explains why our scheduled tasks were dispatched early, but we had to know why our clock wasn't synchronized.&lt;/p&gt;

&lt;p&gt;Using PowerShell, first we check the status of the Windows time service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;w32tm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/status&lt;/span&gt;&lt;span class="w"&gt;
&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;Indicador de salto: 0(ninguna advertencia)
Capa: 4 (referencia secundaria - sincronizada mediante (S)NTP)
Precisión: -23 (119.209ns por tick)
Demora de raíz: 5.0620658s
Dispersión de raíz: 5.1932719s
Id. de referencia: 0xC0A801FC (IP de origen:  192.168.1.252)
Última sincronización de hora correcta: 01/07/2026 13:02:11
Origen: DC1.sertxu.local
Intervalo de sondeo: 12 (4096s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, our server depends on DC1.sertxu.local for the clock synchronization.&lt;/p&gt;

&lt;p&gt;Currently, our scheduler server is synchronizing with DC1, which also seems to have a time drift.&lt;/p&gt;

&lt;p&gt;Let's check the time service on DC1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;w32tm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/status&lt;/span&gt;&lt;span class="w"&gt;
&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;Indicador de salto: 0(ninguna advertencia)
Capa: 4 (referencia secundaria - sincronizada mediante (S)NTP)
Precisión: -23 (119.209ns por tick)
Demora de raíz: 5.0620658s
Dispersión de raíz: 5.1163169s
Id. de referencia: 0xC0A801FC (IP de origen:  192.168.1.252)
Última sincronización de hora correcta: 01/07/2026 13:28:43
Origen: DC1.sertxu.local
Intervalo de sondeo: 10 (1024s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's kind of strange! Our DC1 server depends on itself for clock synchronization, and also has a 5-second drift. That doesn't seem right.&lt;/p&gt;

&lt;p&gt;By default, when a Windows device joins a domain, it starts synchronizing its time with the domain servers.&lt;/p&gt;

&lt;p&gt;But we have to make sure the primary domain server (PDC Emulator) synchronizes its time with a reliable external NTP server.&lt;/p&gt;

&lt;p&gt;First, we have to make sure our DC1 is the primary domain server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;netdom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fsmo&lt;/span&gt;&lt;span class="w"&gt;
&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;Maestro de esquema          DC1.sertxu.local
Maestro nomencl. dominios   DC1.sertxu.local
PDC                         DC1.sertxu.local
Administrador de grupos RID DC1.sertxu.local
Maestro de infraestructura  DC1.sertxu.local
El comando se completó correctamente.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start synchronizing with an external NTP server, we have to update the Windows time service configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;w32tm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/manualpeerlist:&lt;/span&gt;&lt;span class="s2"&gt;"pool.ntp.org"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/syncfromflags:manual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/reliable:yes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/update&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then restart the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;w32time&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;w32time&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our DC1 will synchronize its time with the NTP server pool and will eventually solve its time drift.&lt;/p&gt;

&lt;p&gt;But what will happen with the other servers and computers joined to the domain? Because our DC1 is our primary domain server, the other devices that depend on DC1 will also start to solve the time drift, so after a few minutes all devices will be in sync with the NTP server pool.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>tutorial</category>
      <category>networking</category>
    </item>
    <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://dev.to/sertxudev/deploying-a-fastapi-app-to-kubernetes-with-health-probes-fm5</link>
      <guid>https://dev.to/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://dev.to/sertxudev/switching-a-zebra-printer-to-thermal-transfer-mode-with-zpl-5bjl</link>
      <guid>https://dev.to/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://dev.to/sertxudev/configure-static-ip-address-on-netplan-2am1</link>
      <guid>https://dev.to/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://dev.to/sertxudev/backup-s3-buckets-using-rclone-e6p</link>
      <guid>https://dev.to/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://dev.to/sertxudev/add-an-nfs-mount-point-to-a-ubuntu-server-2k9n</link>
      <guid>https://dev.to/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://dev.to/sertxudev/remove-local-and-local-lvm-from-proxmox-37i6</link>
      <guid>https://dev.to/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://dev.to/sertxudev/registry-crud-actions-on-windows-group-policy-3nji</link>
      <guid>https://dev.to/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://dev.to/sertxudev/disabling-fast-boot-via-windows-registry-23hi</link>
      <guid>https://dev.to/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://dev.to/sertxudev/trust-any-proxy-in-laravel-501n</link>
      <guid>https://dev.to/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://dev.to/sertxudev/trust-proxy-with-traefik-in-k8s-582k</link>
      <guid>https://dev.to/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>
  </channel>
</rss>
