<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux Sin Humo</title><link>https://sergiobelkin.com/</link><description>Linux y Tecnología libres de hypes</description><atom:link href="https://sergiobelkin.com/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>es</language><copyright>Contents © 2026 &lt;a href="mailto:sebelk@gmail.com"&gt;sebelk&lt;/a&gt; 
&lt;a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;
&lt;img alt="Creative Commons License BY-NC-SA"
style="border-width:0; margin-bottom:12px;"
src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png"&gt;&lt;/a&gt;
</copyright><lastBuildDate>Sat, 11 Jul 2026 08:33:12 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Archivos sparse en Linux</title><link>https://sergiobelkin.com/posts/sparse-files-linux/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/sparse-files-mapa.png"&gt;&lt;/figure&gt; &lt;h3 id="sparse-files-en-la-practica"&gt;Sparse files en la práctica&lt;/h3&gt;
&lt;p&gt;Un sparse file se entiende creándolo y mirándolo, no leyendo sobre él. Vamos a tratar un archivo como el disco de una máquina virtual: se crea a tamaño completo y el guest le escribe encima. En la terminal, esas escrituras las simulamos con &lt;code&gt;dd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;El "disco" de 1 GiB, todavía en blanco:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;truncate -s 1G disk.img
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lo medimos de dos maneras —lo que declara y lo que ocupa de verdad—:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;du -h --apparent-size disk.img    # 1,0G  ← lo que declara
du -h disk.img                    # 0     ← lo que ocupa
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Declara un gigabyte y ocupa cero. Escribimos 16 MiB al principio, como el guest grabando su arranque:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;urandom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;notrunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;none&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;du -h --apparent-size disk.img    # 1,0G
du -h disk.img                    # 16M
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;El tamaño declarado no se movió; el real subió los 16 MiB exactos que escribimos. Ahora otros 16 MiB, pero lejos, en el offset 512 MiB:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;urandom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;notrunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;none&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;du -h disk.img                    # 32M
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Real 32 MiB, aparente todavía 1 GiB. Esos 32 MiB reales son solo las dos zonas de 16 que escribimos; entre ellas quedó un &lt;strong&gt;agujero&lt;/strong&gt; de casi 500 MiB —espacio que el archivo declara pero para el que nunca se pidió un bloque en disco—.&lt;/p&gt;
&lt;p&gt;Ahora al derecho y al revés. Llenamos parte del agujero, 128 MiB en el offset 200 MiB:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;urandom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;notrunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;none&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;du -h disk.img                    # 160M
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;El real saltó a 160 MiB —los 32 de antes más los 128 nuevos—: escribir en un agujero cuesta disco. Y lo perforamos de vuelta, con &lt;code&gt;fallocate&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;fallocate --punch-hole --offset 200M --length 128M disk.img
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;du -h disk.img                    # 32M
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;El real volvió a 32 MiB: perforar libera los bloques y los devuelve al disco. Ese tramo, ahora vacío, se lee como ceros —no como basura, ni con error—:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;skip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;none&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'\0'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;wc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;En todo el recorrido el tamaño aparente no se movió de 1 GiB; el real subió al escribir y bajó al perforar. Son las dos medidas de un sparse file: lo que declara y lo que realmente ocupa en disco.&lt;/p&gt;
&lt;p&gt;Las dos direcciones del recorrido, que es lo que conviene no confundir:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operación&lt;/th&gt;
&lt;th&gt;Partís de&lt;/th&gt;
&lt;th&gt;Terminás en&lt;/th&gt;
&lt;th&gt;&lt;code&gt;st_blocks&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;escribir (&lt;code&gt;dd … seek=&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;agujero&lt;/td&gt;
&lt;td&gt;datos en disco&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sube&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;perforar (&lt;code&gt;--punch-hole&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;datos en disco&lt;/td&gt;
&lt;td&gt;agujero (se lee como ceros)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;baja&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Perforar no &lt;em&gt;escribe&lt;/em&gt; ceros: &lt;em&gt;libera&lt;/em&gt; los bloques. Que el agujero se lea después como ceros es el efecto observable; el mecanismo es la desasignación, y por eso el espacio real baja en vez de subir.&lt;/p&gt;
&lt;h3 id="dos-tamanos-no-uno"&gt;Dos tamaños, no uno&lt;/h3&gt;
&lt;p&gt;El lab mostró dos números que se mueven por separado. Tienen nombre, y el inodo guarda los dos; &lt;code&gt;stat&lt;/code&gt; los muestra juntos:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;stat -c 'aparente=%s bytes | reales=%b bloques de %B bytes' disk.img
&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;st_size&lt;/code&gt;&lt;/strong&gt; (el "aparente") es el offset del último byte del archivo más uno. Es lo que responde &lt;code&gt;ls -l&lt;/code&gt;. Es una declaración sobre el espacio de direcciones lógico del archivo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;st_blocks&lt;/code&gt;&lt;/strong&gt; (el "real") es el espacio que el archivo ocupa de verdad, contado —en Linux— en unidades fijas de 512 bytes. Es lo que informa &lt;code&gt;du&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/sparse-files-diagrama.svg"&gt;&lt;img src="https://sergiobelkin.com/images/sparse-files-diagrama.svg" alt="El archivo sparse del lab como una tira de 1 GiB: dos bloques de datos de 16 MiB en terracota, en los offsets 0 y 512 MiB, y el resto agujeros punteados. El tamaño aparente (st_size) es 1 GiB; el real (st_blocks), 32 MiB." style="max-width: 100%; height: auto;"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;El mismo archivo, sus dos medidas: &lt;code&gt;st_size&lt;/code&gt; declara 1 GiB —el ancho lógico completo—; &lt;code&gt;st_blocks&lt;/code&gt; cuenta solo los dos bloques tocados. Entre los datos, agujeros: espacio declarado que no ocupa disco.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Un archivo denso cualquiera muestra las dos magnitudes juntas. &lt;code&gt;/usr/bin/chmod&lt;/code&gt; mide 65920 bytes (&lt;code&gt;st_size&lt;/code&gt;), pero en disco ocupa 69632: el filesystem asigna bloques enteros de 4096, y 65920 bytes necesitan 17 de esos bloques (17 × 4096 = 69632), con 3712 bytes del último reservados pero sin usar.&lt;sup id="fnref:unidades"&gt;&lt;a class="footnote-ref" href="https://sergiobelkin.com/posts/sparse-files-linux/#fn:unidades"&gt;1&lt;/a&gt;&lt;/sup&gt; En un archivo denso lo asignado iguala o supera al tamaño.&lt;/p&gt;
&lt;p&gt;Un sparse file es el caso opuesto: &lt;code&gt;st_size&lt;/code&gt; mayor que el espacio asignado, y la diferencia son los &lt;strong&gt;agujeros&lt;/strong&gt; que vimos abrirse y cerrarse en el lab.&lt;/p&gt;
&lt;h3 id="de-donde-salen-en-la-practica"&gt;De dónde salen en la práctica&lt;/h3&gt;
&lt;p&gt;Los agujeros no son una curiosidad de laboratorio. Aparecen solos, sin que nadie los pida, cada vez que un programa escribe lejos del principio del archivo o libera rangos intermedios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Imágenes de disco de máquinas virtuales.&lt;/strong&gt; Un disco &lt;code&gt;raw&lt;/code&gt; de 100 GiB con 8 GiB usados es un sparse file. &lt;code&gt;qcow2&lt;/code&gt; hace lo suyo por encima, pero el archivo subyacente también lo es.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bases de datos&lt;/strong&gt; que preasignan un archivo grande y lo llenan de a poco.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clientes de BitTorrent&lt;/strong&gt;, que crean el archivo con su tamaño final y van completando piezas fuera de orden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Core dumps&lt;/strong&gt;, que no vuelcan las regiones de memoria sin mapear.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;/var/log/lastlog&lt;/code&gt;&lt;/strong&gt;, que usa el UID del usuario como posición dentro del archivo — el caso más didáctico de todos.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Los filesystems que un sysadmin corporativo tiene enfrente —ext4, XFS, btrfs— soportan agujeros. También &lt;code&gt;tmpfs&lt;/code&gt;. FAT y exFAT no, y son justo los que aparecen en un pendrive o en la partición EFI: copiar un sparse file a uno de ellos lo escribe entero, porque el destino no tiene forma de representar los agujeros.&lt;/p&gt;
&lt;h3 id="ver-donde-estan-los-agujeros"&gt;Ver dónde están los agujeros&lt;/h3&gt;
&lt;p&gt;Para ver &lt;em&gt;dónde&lt;/em&gt; están los agujeros —qué rangos tienen datos y cuáles son hueco—, &lt;code&gt;xfs_io&lt;/code&gt; (del paquete &lt;code&gt;xfsprogs&lt;/code&gt;) los marca con &lt;code&gt;hole&lt;/code&gt;. Se apoya en &lt;code&gt;FIEMAP&lt;/code&gt;, un ioctl genérico del kernel que ext4, XFS y btrfs implementan por igual, así que, a pesar de su nombre, sirve en los tres.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;xfs_io -c fiemap disk.img
  0: [0..32767]: …
  1: [32768..1048575]: hole
  2: [1048576..1081343]: …
  3: [1081344..2097151]: hole
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;La tercera columna —los offsets físicos en disco— la recorté con &lt;code&gt;…&lt;/code&gt;: cambia según el filesystem y no aporta acá. Los rangos van en sectores de 512 bytes: los primeros 16 MiB (&lt;code&gt;[0..32767]&lt;/code&gt;) tienen datos, el tramo &lt;code&gt;[32768..1048575]&lt;/code&gt; es un &lt;code&gt;hole&lt;/code&gt;, en el offset 512 MiB (&lt;code&gt;[1048576..1081343]&lt;/code&gt;) están los otros 16 MiB, y el resto hasta el final es otro &lt;code&gt;hole&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Hay un camino más de bajo nivel: los flags &lt;code&gt;SEEK_HOLE&lt;/code&gt; y &lt;code&gt;SEEK_DATA&lt;/code&gt; de &lt;code&gt;lseek&lt;/code&gt; (desde Linux 3.1) saltan al próximo agujero o al próximo rango con datos sin leer el archivo entero. Es el mecanismo que usan por dentro las herramientas que preservan agujeros al copiar, y el que las separa de las que re-inflan.&lt;/p&gt;
&lt;p&gt;Una advertencia sobre &lt;code&gt;filefrag&lt;/code&gt;, que suele ser lo primero que uno prueba: su conteo (&lt;code&gt;N extents found&lt;/code&gt;) mide fragmentación física, no agujeros, y se lee distinto según el filesystem —el mismo archivo con el mismo agujero da "1 extent" en XFS y "2" en btrfs—. Para ver agujeros, &lt;code&gt;fiemap&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="convertir-ceros-en-agujeros"&gt;Convertir ceros en agujeros&lt;/h4&gt;
&lt;p&gt;En el lab abrimos agujeros con &lt;code&gt;--punch-hole&lt;/code&gt;. La otra variante, &lt;code&gt;--dig-holes&lt;/code&gt;, recupera espacio: toma un archivo que ya ocupa disco lleno de ceros —una copia que se infló, una imagen descargada sin sparse— y reemplaza esas secuencias de ceros por agujeros. Un archivo de 64 MiB escritos en cero ocupa 64 MiB reales; después, cero:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;zero&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;lleno&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;none&lt;/span&gt;
&lt;span class="nv"&gt;du&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lleno&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="w"&gt;                   &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;
&lt;span class="nv"&gt;fallocate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;holes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lleno&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;
&lt;span class="nv"&gt;du&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lleno&lt;/span&gt;.&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="w"&gt;                   &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;aparente&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sigue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;en&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Con &lt;code&gt;--dig-holes&lt;/code&gt; se cierra el cuadro de las tres operaciones. &lt;code&gt;--punch-hole&lt;/code&gt; y &lt;code&gt;--dig-holes&lt;/code&gt; terminan igual —un agujero, &lt;code&gt;st_blocks&lt;/code&gt; baja—, pero parten de cosas distintas:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operación&lt;/th&gt;
&lt;th&gt;Partís de&lt;/th&gt;
&lt;th&gt;Terminás en&lt;/th&gt;
&lt;th&gt;&lt;code&gt;st_blocks&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;escribir ceros (&lt;code&gt;dd if=/dev/zero&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;agujero&lt;/td&gt;
&lt;td&gt;ceros reales en disco&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sube&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;perforar (&lt;code&gt;--punch-hole&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;datos en disco&lt;/td&gt;
&lt;td&gt;agujero&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;baja&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cavar (&lt;code&gt;--dig-holes&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;ceros reales en disco&lt;/td&gt;
&lt;td&gt;agujero&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;baja&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Escribir ceros y cavarlos son inversas: la primera los materializa en disco, la segunda los recupera. Perforar es el atajo cuando ni siquiera te importa qué había en el rango.&lt;/p&gt;
&lt;h3 id="cuando-se-convierte-en-un-problema"&gt;Cuándo se convierte en un problema&lt;/h3&gt;
&lt;p&gt;El diseño se sostiene mientras el archivo se quede quieto. El problema aparece cuando algo lo &lt;strong&gt;copia&lt;/strong&gt;, porque una herramienta que lee a través de la API normal de bytes ve ceros, no agujeros. Si al escribir no vuelve a perforarlos, los materializa.&lt;/p&gt;
&lt;p&gt;Con el &lt;code&gt;disk.img&lt;/code&gt; de arriba —1 GiB aparente, 32 MiB reales—, copiado en RHEL 8 (XFS, coreutils 8.30):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operación&lt;/th&gt;
&lt;th&gt;Ocupación en destino&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cp disk.img destino&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;64 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cp --sparse=never disk.img destino&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat disk.img &amp;gt; destino&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tar cf plain.tar disk.img&lt;/code&gt; y extraer&lt;/td&gt;
&lt;td&gt;1 GiB (y el &lt;code&gt;.tar&lt;/code&gt; pesa 1,1 GiB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tar cSf sparse.tar disk.img&lt;/code&gt; y extraer&lt;/td&gt;
&lt;td&gt;64 MiB (y el &lt;code&gt;.tar&lt;/code&gt; pesa 33 MiB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rsync -a disk.img destino&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rsync -aS disk.img destino&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;64 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Las cuatro que re-inflan —&lt;code&gt;cp --sparse=never&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt; sin &lt;code&gt;-S&lt;/code&gt;, &lt;code&gt;rsync -a&lt;/code&gt;— llevan la copia a 1 GiB: materializan cada cero.&lt;sup id="fnref:fs"&gt;&lt;a class="footnote-ref" href="https://sergiobelkin.com/posts/sparse-files-linux/#fn:fs"&gt;2&lt;/a&gt;&lt;/sup&gt; Las tres que preservan el sparse —&lt;code&gt;cp&lt;/code&gt; a secas, &lt;code&gt;tar -S&lt;/code&gt;, &lt;code&gt;rsync -aS&lt;/code&gt;— dan 64 MiB. Que &lt;code&gt;cp&lt;/code&gt; sin flags preserve ya es contraintuitivo: su default es &lt;code&gt;--sparse=auto&lt;/code&gt;, detecta los agujeros del origen y los recrea; solo los pierde con &lt;code&gt;--sparse=never&lt;/code&gt; o si el destino no los soporta.&lt;/p&gt;
&lt;p&gt;Pero 64 MiB es el &lt;strong&gt;doble&lt;/strong&gt; de los 32 del origen, y no es re-inflado: los agujeros están intactos (&lt;code&gt;xfs_io -c fiemap destino&lt;/code&gt; los muestra igual), y ese doble es XFS reservando bloques de más al escribir (&lt;em&gt;speculative preallocation&lt;/em&gt;). Es una reserva transitoria —XFS la reclama con el tiempo (el barredor de &lt;code&gt;eofblocks&lt;/code&gt;) o bajo presión de espacio, y su tamaño depende de la opción de montaje &lt;code&gt;allocsize&lt;/code&gt; y de la versión—, así que el número exacto puede no reproducirse igual en otro XFS. En ext4 o btrfs la misma copia da los 32 MiB exactos: es un rasgo de XFS, no de las herramientas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;tar&lt;/code&gt; sin &lt;code&gt;-S&lt;/code&gt; infla también el archivo &lt;code&gt;.tar&lt;/code&gt;.&lt;/strong&gt; Produce uno de 1,1 GiB en lugar de los 33 MiB que ocupa con &lt;code&gt;-S&lt;/code&gt;. Ese gigabyte de ceros viaja por la red, se escribe en la cinta o en el bucket, y recién se nota cuando el job de backup llena el volumen de destino.&lt;/p&gt;
&lt;p&gt;Con &lt;code&gt;gzip&lt;/code&gt;, &lt;code&gt;bzip2&lt;/code&gt; o &lt;code&gt;xz&lt;/code&gt; la falla es más difícil de ver, porque un gigabyte de ceros comprime a casi nada. El &lt;code&gt;.gz&lt;/code&gt; es minúsculo y todo parece correcto. El &lt;code&gt;gunzip&lt;/code&gt; en el destino escribe el gigabyte completo, sin agujeros.&lt;/p&gt;
&lt;p&gt;De las herramientas de transferencia, &lt;code&gt;scp&lt;/code&gt; y &lt;code&gt;sftp&lt;/code&gt; no tienen ninguna noción de agujeros. Tampoco &lt;code&gt;git&lt;/code&gt;: el blob incluye todos los ceros. &lt;code&gt;dd&lt;/code&gt; los re-infla salvo que le pases &lt;code&gt;conv=sparse&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="la-regla-que-se-deduce-de-la-tabla"&gt;La regla que se deduce de la tabla&lt;/h3&gt;
&lt;p&gt;Si una herramienta ofrece un flag explícito de sparse —&lt;code&gt;rsync -S&lt;/code&gt;, &lt;code&gt;tar -S&lt;/code&gt;, &lt;code&gt;dd conv=sparse&lt;/code&gt;, &lt;code&gt;cp --sparse=always&lt;/code&gt;, &lt;code&gt;restic restore --sparse&lt;/code&gt;, &lt;code&gt;borg extract --sparse&lt;/code&gt;— es señal de que &lt;strong&gt;por defecto no lo hace&lt;/strong&gt;, o de que su default es una heurística que conviene verificar. Las que ni siquiera ofrecen la opción re-inflan siempre.&lt;/p&gt;
&lt;p&gt;En backup empresarial el patrón se repite: Bacula y Bareos manejan agujeros solo si activás la directiva &lt;code&gt;Sparse = yes&lt;/code&gt;, que viene desactivada. &lt;code&gt;restic&lt;/code&gt; y &lt;code&gt;borg&lt;/code&gt; deduplican los ceros muy bien —el repositorio queda chico— pero la restauración los re-infla si no le pedís lo contrario.&lt;/p&gt;
&lt;p&gt;Hay una excepción importante, para no exagerar el problema: el backup &lt;strong&gt;a nivel de imagen o de bloque&lt;/strong&gt; no lee archivos, lee bloques asignados del dispositivo. Los agujeros no le representan un problema. El riesgo descrito acá aplica al backup y la restauración &lt;strong&gt;a nivel de archivo&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;El origen nunca se queja: el espacio real de un sparse file es minúsculo. La medición que importa no es la del archivo que tenés, sino la del que vas a producir —el destino de la copia, el &lt;code&gt;.tar&lt;/code&gt; que se sube, el volumen donde se restaura—. Un &lt;code&gt;du -h&lt;/code&gt; contra un &lt;code&gt;du -h --apparent-size&lt;/code&gt; en el origen te dice cuánto puede crecer; los flags de la herramienta te dicen si va a crecer.&lt;/p&gt;
&lt;h3 id="fuentes-y-mas-recursos"&gt;Fuentes y más recursos&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man3/stat.3type.html"&gt;&lt;code&gt;stat(3type)&lt;/code&gt;&lt;/a&gt; — define los campos de &lt;code&gt;struct stat&lt;/code&gt;. Sobre &lt;code&gt;st_blocks&lt;/code&gt;: "the number of blocks allocated to the file, in 512-byte units". El &lt;code&gt;stat(2)&lt;/code&gt; advierte que esa unidad no es universal (HP-UX cuenta en 1024, AIX en 4K); en Linux es 512.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man2/lseek.2.html"&gt;&lt;code&gt;lseek(2)&lt;/code&gt;&lt;/a&gt; — define &lt;code&gt;SEEK_HOLE&lt;/code&gt; y &lt;code&gt;SEEK_DATA&lt;/code&gt;, el mecanismo que permite recorrer agujeros sin leer el archivo entero. Disponibles desde Linux 3.1.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man1/fallocate.1.html"&gt;&lt;code&gt;fallocate(1)&lt;/code&gt;&lt;/a&gt; — &lt;code&gt;--punch-hole&lt;/code&gt; para perforar un rango y &lt;code&gt;--dig-holes&lt;/code&gt; para convertir en agujeros las secuencias de ceros ya escritas.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/filefrag.8.html"&gt;&lt;code&gt;filefrag(8)&lt;/code&gt;&lt;/a&gt; — inspecciona el mapa de extents de un archivo; viene en &lt;code&gt;e2fsprogs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;man 1 cp&lt;/code&gt; — la sección de &lt;code&gt;--sparse&lt;/code&gt; aclara que la detección por defecto (&lt;code&gt;auto&lt;/code&gt;) usa "un mecanismo heurístico muy básico": por eso conviene medir en lugar de asumir.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;man 1 tar&lt;/code&gt; — &lt;code&gt;-S&lt;/code&gt;/&lt;code&gt;--sparse&lt;/code&gt; y &lt;code&gt;--hole-detection=seek|raw&lt;/code&gt;, que usa &lt;code&gt;SEEK_HOLE&lt;/code&gt; cuando puede y cae a leer el archivo crudo cuando no.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://restic.readthedocs.io/en/stable/050_restore.html"&gt;Restauración con restic&lt;/a&gt; — documenta que "By default, restic does not restore files as sparse"; hay que pasar &lt;code&gt;--sparse&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://borgbackup.readthedocs.io/en/stable/usage/extract.html"&gt;&lt;code&gt;borg extract&lt;/code&gt;&lt;/a&gt; — su opción &lt;code&gt;--sparse&lt;/code&gt; recrea los agujeros a partir de los bloques de ceros.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:unidades"&gt;
&lt;p&gt;Ese mismo espacio asignado lo expresa cada herramienta en su propia unidad, y los números no siempre coinciden: &lt;code&gt;stat&lt;/code&gt; da &lt;code&gt;st_blocks = 136&lt;/code&gt; (136 × 512 = 69632), &lt;code&gt;ls -ls&lt;/code&gt; pone &lt;code&gt;68&lt;/code&gt; en su primera columna (68 × 1024 = 69632) y &lt;code&gt;du&lt;/code&gt; dice &lt;code&gt;68K&lt;/code&gt;. La diferencia entre 136 y 68 es solo la unidad —512 bytes contra 1024, el default de GNU &lt;code&gt;ls&lt;/code&gt;—. Con &lt;code&gt;POSIXLY_CORRECT&lt;/code&gt;, o con &lt;code&gt;ls -s --block-size=512&lt;/code&gt;, &lt;code&gt;ls -s&lt;/code&gt; baja a 512 y coincide con &lt;code&gt;st_blocks&lt;/code&gt;. &lt;a class="footnote-backref" href="https://sergiobelkin.com/posts/sparse-files-linux/#fnref:unidades" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:fs"&gt;
&lt;p&gt;En btrfs con compresión (el default de Fedora) esas cifras de 1 GiB salen mucho más chicas: los ceros reescritos se comprimen a casi nada. El archivo se re-infla igual en el espacio lógico, pero el filesystem disimula el costo en disco. &lt;a class="footnote-backref" href="https://sergiobelkin.com/posts/sparse-files-linux/#fnref:fs" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>backup</category><category>filesystems</category><category>sparse-files</category><category>sysadmin</category><guid>https://sergiobelkin.com/posts/sparse-files-linux/</guid><pubDate>Fri, 10 Jul 2026 10:00:00 GMT</pubDate></item><item><title>Intel y AMD: quién fija el límite de potencia del CPU</title><link>https://sergiobelkin.com/posts/intel-amd-control-temperatura-frecuencia/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/intel-amd-control-temperatura-frecuencia.webp"&gt;&lt;/figure&gt; &lt;p&gt;Hace unas semanas conté cómo limité térmicamente una notebook Intel usada como CPU box en clamshell —tapa cerrada, monitor portátil encima, la máquina debajo de todo—: &lt;a href="https://sergiobelkin.com/posts/gestion-termica-clamshell-rapl-tuned/"&gt;Gestión térmica en notebook clamshell con RAPL y tuned&lt;/a&gt;. La idea era bajar a mano el techo de potencia para que el CPU no entrara en throttling (que no se frenara solo al recalentarse).&lt;/p&gt;
&lt;p&gt;Cuando fui a repetir lo mismo en un equipo &lt;strong&gt;AMD Ryzen&lt;/strong&gt;, el ejercicio derivó en algo más interesante que portar una receta. Intel y AMD no se diferencian solo en &lt;em&gt;cómo&lt;/em&gt; se toca el límite: se diferencian en &lt;strong&gt;quién puede fijar el límite&lt;/strong&gt; de la potencia y la frecuencia — el hardware solo, o también vos. Y esa diferencia, no el clamshell, es lo que vale la pena contar.&lt;/p&gt;
&lt;p&gt;El clamshell quedó como lo que era: el escenario que me obligó a medir. Lo uso de banco de pruebas, pero el tema es la comparación.&lt;/p&gt;
&lt;h3 id="como-controla-intel-el-limite-lo-escribis-vos"&gt;Cómo controla Intel: el límite lo escribís vos&lt;/h3&gt;
&lt;p&gt;En Intel, el techo de potencia es algo que &lt;strong&gt;escribís vos&lt;/strong&gt;. RAPL (&lt;em&gt;Running Average Power Limit&lt;/em&gt;) expone dos límites de potencia: PL1, el techo sostenido que el chip mantiene en el tiempo, y PL2, uno más alto que se permite por ráfagas cortas (el boost). Viven en el powercap framework del kernel —la interfaz bajo &lt;code&gt;/sys/class/powercap&lt;/code&gt; para poner topes de potencia— y son de lectura/escritura:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tee&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;powercap&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;intel&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rapl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;constraint_0_power_limit_uw&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ese &lt;code&gt;echo&lt;/code&gt; baja la potencia disponible y el PCU (&lt;em&gt;Power Control Unit&lt;/em&gt;) del CPU la respeta. El hardware sigue teniendo su propia gestión térmica, pero &lt;strong&gt;ese tope es un control compartido&lt;/strong&gt;: el sysadmin puede ponerlo por debajo del default de fábrica, y el procesador lo aplica.&lt;/p&gt;
&lt;h3 id="como-controla-amd-el-limite-lo-decide-el-firmware"&gt;Cómo controla AMD: el límite lo decide el firmware&lt;/h3&gt;
&lt;p&gt;En AMD ese control, sencillamente, no te lo dan. La rama de powercap puede existir —los Ryzen modernos exponen RAPL— pero con una diferencia decisiva:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En AMD, el RAPL del powercap &lt;strong&gt;reporta energía consumida&lt;/strong&gt; (&lt;code&gt;energy_uj&lt;/code&gt;), pero los archivos de límite de potencia (&lt;code&gt;constraint_*_power_limit_uw&lt;/code&gt;) no se exponen. El máximo de potencia no se fija desde el sistema operativo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No es un problema de permisos: el kernel no expone esa escritura para AMD por ninguna vía mainline (lo que viene de fábrica en el kernel oficial). Y hasta la &lt;em&gt;lectura&lt;/em&gt; de energía está acotada: el &lt;code&gt;energy_uj&lt;/code&gt; que aparece más abajo es legible solo por root desde el kernel 5.10, como mitigación de un ataque de canal lateral (PLATYPUS). Escritura del límite, en cambio, nunca hubo. El gobierno de la potencia y la frecuencia ocurre &lt;strong&gt;adentro del SMU&lt;/strong&gt; (&lt;em&gt;System Management Unit&lt;/em&gt;), el microcontrolador del SoC, que regula solo, en tiempo real, contra el primero de varios límites que se sature —potencia, corriente y temperatura—. El sistema operativo no hace esa regulación fina ni escribe el límite de potencia, pero tampoco es un espectador pasivo: le fija la &lt;strong&gt;política&lt;/strong&gt; dentro de la cual optimizar —el gobernador, la preferencia de eficiencia o rendimiento (EPP) y el perfil de energía— y lee la telemetría.&lt;/p&gt;
&lt;p&gt;Comprobalo en tu propia máquina. En vez de apuntar a un archivo puntual, listá el contenido de la zona del paquete y mirá qué hay:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;cat /sys/class/powercap/intel-rapl:0/name
ls -l /sys/class/powercap/intel-rapl:0/
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Dos cosas para notar. La primera: el dominio se llama &lt;code&gt;intel-rapl&lt;/code&gt; aun en una máquina AMD. No es un error — el soporte RAPL de AMD se registra reusando el mismo framework y el mismo nombre que ya existía en el kernel. La segunda, y es el punto: en mi equipo Ryzen el &lt;code&gt;name&lt;/code&gt; da &lt;code&gt;package-0&lt;/code&gt; y adentro solo hay &lt;code&gt;energy_uj&lt;/code&gt; (el contador de energía consumida, de lectura). No existe &lt;strong&gt;ningún&lt;/strong&gt; archivo &lt;code&gt;constraint_*&lt;/code&gt; — ni &lt;code&gt;constraint_0_power_limit_uw&lt;/code&gt; ni sus variantes. En un Intel, esa misma carpeta tendría los &lt;code&gt;constraint_0_*&lt;/code&gt; (PL1) y &lt;code&gt;constraint_1_*&lt;/code&gt; (PL2): los archivos donde se escribe el techo de potencia. En AMD el concepto de límite ni se expone. RAPL mide; no limita. Y es así por cómo está implementado el driver de AMD en mainline, no por una particularidad de este equipo —aunque no probé cada combinación de kernel y BIOS.&lt;/p&gt;
&lt;h3 id="la-evidencia-que-hace-el-firmware-de-amd-bajo-carga"&gt;La evidencia: qué hace el firmware de AMD bajo carga&lt;/h3&gt;
&lt;p&gt;Medí el equipo AMD en clamshell y en AC —la condición real de un CPU box: tapa cerrada, en corriente, con un monitor de 14" apoyado encima bloqueando el aire— con un baseline en reposo y un stress de 20 minutos sostenidos (&lt;code&gt;stress-ng --cpu 0&lt;/code&gt;), registrando temperatura de paquete (&lt;code&gt;Tctl&lt;/code&gt;, el sensor que reporta AMD vía &lt;code&gt;k10temp&lt;/code&gt;) y frecuencia.&lt;/p&gt;
&lt;p&gt;En reposo, ~40 °C. Bajo carga, la temperatura sube despacio y se asienta cerca de 73 °C —todavía trepando muy de a poco al cabo de los 20 minutos—, mientras la frecuencia se mantiene alrededor de ~3,0 GHz toda la corrida, sin caídas abruptas:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;59.2&lt;/span&gt;&lt;span class="err"&gt;°&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3044.129&lt;/span&gt;&lt;span class="n"&gt;MHz&lt;/span&gt;
&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;69.0&lt;/span&gt;&lt;span class="err"&gt;°&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3019.180&lt;/span&gt;&lt;span class="n"&gt;MHz&lt;/span&gt;
&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;71.2&lt;/span&gt;&lt;span class="err"&gt;°&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3019.166&lt;/span&gt;&lt;span class="n"&gt;MHz&lt;/span&gt;
&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;73.4&lt;/span&gt;&lt;span class="err"&gt;°&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3019.182&lt;/span&gt;&lt;span class="n"&gt;MHz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Dos cosas para leer de ahí:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;El firmware sostiene un punto de operación estable de ~3,0 GHz con todos los núcleos cargados, sin que nadie intervenga.&lt;/strong&gt; No es un techo que le puse yo: &lt;code&gt;scaling_max_freq&lt;/code&gt; estaba en el máximo de fábrica. Es el SMU manteniendo la frecuencia que la potencia disponible le permite sostener.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La temperatura se queda lejos de cualquier límite.&lt;/strong&gt; 73 °C, lejos de su límite térmico —el Tjmax: la temperatura a la que el chip se frena o se apaga para no dañarse, del orden de 90–100 °C según el modelo de Ryzen—: margen de sobra. Y en 20 minutos no hubo una sola caída abrupta de frecuencia: el firmware regula y se queda quieto.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="que-significa-la-diferencia"&gt;Qué significa la diferencia&lt;/h3&gt;
&lt;p&gt;Dos formas de repartir el control:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;En Intel el control se reparte.&lt;/strong&gt; El PCU regula, pero te deja escribir el techo de potencia en mainline (PL1/PL2): podés bajar el límite por debajo del default de fábrica y el procesador lo respeta. Queda compartido entre el hardware y vos. En el post Intel fijé PL1 bajo y &lt;code&gt;tuned&lt;/code&gt; activo, y la frecuencia se sostuvo estable en clamshell — aunque, en realidad, apliqué las dos cosas juntas y no aislé cuánto aportó cada una.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;En AMD el control queda en el firmware.&lt;/strong&gt; El SMU regula de forma autónoma; el sistema operativo le marca una preferencia —hacia eficiencia o rendimiento, vía EPP y el perfil de energía— pero no le escribe el límite de potencia. Para ponerle un techo duro desde mainline, lo único que queda es la &lt;strong&gt;frecuencia&lt;/strong&gt; (&lt;code&gt;scaling_max_freq&lt;/code&gt;). Pero —y esto es lo que muestran las mediciones— el firmware ya está haciendo la regulación: en 20 minutos de carga sostuvo la frecuencia estable por su cuenta, sin que yo tocara nada.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;De esto se sigue algo que va a contramano del reflejo de optimizar:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En este equipo AMD, bajar el techo de frecuencia no solo es &lt;strong&gt;innecesario&lt;/strong&gt; (el firmware ya regula solo) — es &lt;strong&gt;contraproducente&lt;/strong&gt;: le quitarías rendimiento que el chip podía sostener, sin ganar nada en seguridad térmica.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;La receta de RAPL no se porta a AMD, y no solo porque la API sea distinta (el powercap de AMD solo expone telemetría, sin archivos de límite). Y el problema que la justificaba, en este hardware, tampoco existe: el firmware moderno de AMD gobierna el clamshell solo.&lt;/p&gt;
&lt;h3 id="si-igual-queres-meter-mano-en-amd"&gt;Si igual querés meter mano en AMD&lt;/h3&gt;
&lt;p&gt;Para cerrar, las dos vías reales y su estado:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Techo de frecuencia (&lt;code&gt;scaling_max_freq&lt;/code&gt;, mainline).&lt;/strong&gt; Es lo que el kernel te da, sin agregar nada, para ponerle un techo duro: un &lt;code&gt;echo&lt;/code&gt; por core, persistible al boot (un &lt;em&gt;profile&lt;/em&gt; de &lt;code&gt;tuned&lt;/code&gt; o un service propio), sin tocar voltaje. Útil &lt;strong&gt;si tu equipo sí oscila&lt;/strong&gt; o si querés sacrificar rendimiento a propósito por un equipo más fresco. Por lo que vimos, para seguridad térmica acá sobra.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;El tope de potencia (PPT vía &lt;code&gt;ryzenadj&lt;/code&gt;).&lt;/strong&gt; Es la forma de llegar a ese tope del SMU desde el sistema —los límites que AMD llama PPT (&lt;em&gt;Package Power Tracking&lt;/em&gt;, el tope de potencia del paquete) y STAPM (atado a la temperatura de la carcasa)— pero queda &lt;strong&gt;fuera de mainline&lt;/strong&gt;: &lt;code&gt;ryzenadj&lt;/code&gt; (LGPL-3.0) se compila a mano y necesita el módulo &lt;code&gt;ryzen_smu&lt;/code&gt; (GPL-2.0), que vive &lt;em&gt;out-of-tree&lt;/em&gt; —fuera del árbol del kernel— y hay que recompilar en cada actualización (eso lo automatiza DKMS), además de pelear con Secure Boot. Es libre y se usa en producción en handhelds (Bazzite, Steam Deck), pero es tooling de entusiasta, no de infraestructura. Y es justamente la prueba de la tesis: &lt;strong&gt;el control que Intel expone en mainline, en AMD hay que buscarlo fuera del árbol.&lt;/strong&gt; Nada de esto es peligroso —es runtime, no flashea firmware, un reboot revierte todo— pero para un CPU box que tiene que comportarse como infraestructura, el costo no se justifica.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="contrastar-la-premisa-y-despues-medir"&gt;Contrastar la premisa y después medir&lt;/h3&gt;
&lt;p&gt;El reflejo sería asumir que el setup Intel se porta cambiando una ruta de &lt;code&gt;/sys&lt;/code&gt;. Pero la premisa hay que contrastarla, y no es la misma en los dos fabricantes: en AMD el archivo para establecer un límite ni siquiera está. Contrastarla a tiempo es lo que evita el desvío siguiente —buscar &lt;em&gt;la herramienta que sí escribe watts&lt;/em&gt; y terminar en &lt;code&gt;ryzenadj&lt;/code&gt; con un módulo out-of-tree—. Y antes de salir a buscar esa herramienta, conviene medir: acá la medición mostró que no había nada que arreglar.&lt;/p&gt;
&lt;p&gt;La diferencia de arquitectura entre Intel y AMD define si te toca intervenir o si el firmware ya lo hace.&lt;/p&gt;
&lt;h3 id="lo-que-me-llevo"&gt;Lo que me llevo&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Intel y AMD difieren en quién escribe el límite de potencia: Intel te lo deja en mainline (RAPL r/w); AMD lo retiene en el SMU —desde el SO le marcás una preferencia (eficiencia o rendimiento) y leés energía, pero el tope no lo escribís—.&lt;/li&gt;
&lt;li&gt;En AMD, el firmware sostiene una frecuencia estable por su cuenta (~3,0 GHz en 20 min de carga) y la temperatura se queda con margen de sobra respecto del Tjmax. Sin oscilación ni throttling.&lt;/li&gt;
&lt;li&gt;En AMD el tope de potencia (PPT) no se escribe en mainline —solo con tooling out-of-tree (&lt;code&gt;ryzenadj&lt;/code&gt;)—; en mainline tenés la política (EPP, perfil de energía) y un techo por la frecuencia (&lt;code&gt;scaling_max_freq&lt;/code&gt;), pero no el límite de watts directo. Es la contracara exacta de lo que Intel expone de fábrica.&lt;/li&gt;
&lt;li&gt;Antes de optimizar, medir: en este equipo, bajar el techo habría sido contraproducente.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tuned&lt;/code&gt; cruza sin cambios entre plataformas — solo cambia el driver de scaling debajo (&lt;code&gt;amd_pstate&lt;/code&gt; en vez de &lt;code&gt;intel_pstate&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="fuentes"&gt;Fuentes&lt;/h3&gt;
&lt;p&gt;Documentación oficial y mediciones en el propio equipo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentación oficial&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Framework powercap del kernel (RAPL, zonas, &lt;code&gt;constraint_*&lt;/code&gt; de lectura/escritura en Intel): &lt;a href="https://docs.kernel.org/power/powercap/powercap.html"&gt;Power Capping Framework&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Driver &lt;code&gt;amd-pstate&lt;/code&gt; y sus modos (active/EPP): &lt;a href="https://docs.kernel.org/admin-guide/pm/amd-pstate.html"&gt;amd-pstate CPU Performance Scaling Driver&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Restricción de &lt;code&gt;energy_uj&lt;/code&gt; a root como mitigación de canal lateral: &lt;a href="https://platypusattack.com/"&gt;PLATYPUS&lt;/a&gt; y &lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2020-12912"&gt;CVE-2020-12912&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ryzenadj&lt;/code&gt; (LGPL-3.0; PPT/STAPM; necesita el módulo &lt;code&gt;ryzen_smu&lt;/code&gt;): &lt;a href="https://github.com/FlyGoat/RyzenAdj"&gt;FlyGoat/RyzenAdj&lt;/a&gt;. Módulo &lt;code&gt;ryzen_smu&lt;/code&gt; (GPL-2.0, out-of-tree): &lt;a href="https://github.com/leogx9r/ryzen_smu"&gt;leogx9r/ryzen_smu&lt;/a&gt;; para hardware reciente (Zen 5), forks activos como &lt;a href="https://github.com/amkillam/ryzen_smu"&gt;amkillam/ryzen_smu&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Uso de &lt;code&gt;ryzenadj&lt;/code&gt; + &lt;code&gt;ryzen_smu&lt;/code&gt; en handhelds (undervolt del Steam Deck, vía un service con config en &lt;code&gt;/etc/default/ryzenadj&lt;/code&gt;): &lt;a href="https://github.com/ublue-os/bazzite"&gt;Bazzite&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Tjmax exacto por procesador (depende del modelo): &lt;a href="https://www.amd.com/en/products/specifications/processors.html"&gt;especificaciones de AMD&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Verificado en el equipo&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Que el powercap de AMD solo expone &lt;code&gt;energy_uj&lt;/code&gt; y ningún &lt;code&gt;constraint_*&lt;/code&gt;, y la jerarquía &lt;code&gt;package-0&lt;/code&gt;/&lt;code&gt;core&lt;/code&gt;: los comandos &lt;code&gt;cat&lt;/code&gt;/&lt;code&gt;ls&lt;/code&gt; sobre &lt;code&gt;/sys/class/powercap/&lt;/code&gt; que están en el cuerpo. El driver (&lt;code&gt;amd-pstate-epp&lt;/code&gt;) y las palancas de política (EPP, &lt;code&gt;platform_profile&lt;/code&gt;) se leen read-only de &lt;code&gt;/sys/devices/system/cpu/cpufreq/&lt;/code&gt; y &lt;code&gt;/sys/firmware/acpi/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Que el firmware sostiene la frecuencia sin intervención: baseline y stress de 20 minutos, reproducibles con &lt;code&gt;stress-ng --cpu 0&lt;/code&gt; y la lectura de &lt;code&gt;Tctl&lt;/code&gt; (&lt;code&gt;k10temp&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;</description><category>amd-pstate</category><category>monitoreo</category><category>power-management</category><category>sysadmin</category><guid>https://sergiobelkin.com/posts/intel-amd-control-temperatura-frecuencia/</guid><pubDate>Tue, 23 Jun 2026 04:03:53 GMT</pubDate></item><item><title>3 Power Tips + Power Link I12</title><link>https://sergiobelkin.com/posts/3-power-tips-power-link-i12/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/PowerTipsPlus.png"&gt;&lt;/figure&gt; &lt;p&gt;"GUI vs CLI" es un falso dilema. Antes que técnico, es cultural: hábitos, modos de trabajo y supersticiones que arrastramos quienes trabajamos en IT. La consola y la interfaz gráfica no se disputan el mismo lugar; resuelven problemas distintos y, bien usadas, se complementan.&lt;/p&gt;
&lt;h3 id="power-tip-1-una-imagen-contra-una-tabla-de-sectores"&gt;Power Tip #1 Una imagen contra una tabla de sectores&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Hay que redimensionar una partición con datos, y querés ver cómo queda el layout antes de escribir al disco.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;La línea de comandos no te deja sin red: &lt;code&gt;sgdisk&lt;/code&gt; o un playbook de Ansible pueden simular el cambio sin escribir al disco. El tema es cómo lo muestran: una tabla de números —alineación a sectores, espacio libre real, orden físico de las particiones— que hay que reconstruir de cabeza.&lt;/p&gt;
&lt;p&gt;GParted o KDE Partition Manager muestran ese mismo estado final, pero dibujado:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;gparted&lt;span class="w"&gt; &lt;/span&gt;/dev/sdX
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Cargan el layout, dejan encolar varias operaciones, y muestran el resultado antes de aplicarlo. El "Aplicar" recién dispara las llamadas reales al disco.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/pt12-kde-partition-manager.webp"&gt;&lt;img src="https://sergiobelkin.com/images/pt12-kde-partition-manager.webp" alt="Barra de herramientas del Gestor de Particiones de KDE con «Aplicar» y «Cambiar el tamaño/mover», y la lista de particiones del disco: los cambios se preparan sobre el layout y nada se escribe hasta pulsar «Aplicar»"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cuando la operación es difícilmente reversible y conviene evaluar el estado de antemano, la GUI es un camino apropiado: no porque la CLI no pueda simular, sino porque entrega una imagen donde la consola entrega una tabla. Y eso no tiene que ver con el nivel del operador.&lt;/p&gt;
&lt;h3 id="power-tip-2-el-clic-no-se-copia-de-manera-natural"&gt;Power Tip #2 El clic no se copia de manera natural&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ajustaste una regla de firewall desde Cockpit, el servicio volvió, y treinta días después hay que replicarlo en otros 50 nodos.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;El cambio se hizo en la web UI, no en una shell.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;firewall-cmd
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No aparece nada: no hay un comando para copiar.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;firewall-cmd&lt;span class="w"&gt; &lt;/span&gt;--permanent&lt;span class="w"&gt; &lt;/span&gt;--add-service&lt;span class="o"&gt;=&lt;/span&gt;https&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;firewall-cmd&lt;span class="w"&gt; &lt;/span&gt;--reload
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;La CLI permite reproducir y adaptar; la GUI facilita hacerlo una sola vez.&lt;/p&gt;
&lt;h3 id="power-tip-3-un-comando-contra-el-asistente-de-media-docena-de-pantallas"&gt;Power Tip #3 Un comando contra el asistente de media docena de pantallas&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Hay que crear el mismo private endpoint en dev, staging y prod, y en el Portal es un asistente de varias pantallas cada vez.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resource group, región, sub-resource, VNet, subnet: cada pantalla es un clic y un punto donde el tercer entorno termina distinto del primero.&lt;/li&gt;
&lt;li&gt;No hay forma de correr "lo mismo" dos veces: cada pasada se arma a mano de nuevo.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;az&lt;span class="w"&gt; &lt;/span&gt;network&lt;span class="w"&gt; &lt;/span&gt;private-endpoint&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;pe-app&lt;span class="w"&gt; &lt;/span&gt;--resource-group&lt;span class="w"&gt; &lt;/span&gt;rg-prod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--vnet-name&lt;span class="w"&gt; &lt;/span&gt;vnet-prod&lt;span class="w"&gt; &lt;/span&gt;--subnet&lt;span class="w"&gt; &lt;/span&gt;snet-data&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--private-connection-resource-id&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$sqlid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--group-id&lt;span class="w"&gt; &lt;/span&gt;sqlServer&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--connection-name&lt;span class="w"&gt; &lt;/span&gt;pe-app-conn
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;La misma operación entra en una línea parametrizable: la repetís idéntica en cada entorno cambiando un par de variables. El patrón no es de Azure — &lt;code&gt;aws&lt;/code&gt; y &lt;code&gt;gcloud&lt;/code&gt; resuelven lo suyo igual.&lt;/p&gt;
&lt;p&gt;Cuando la misma operación se repite entre entornos, el asistente visual es el cuello de botella, no la ayuda.&lt;/p&gt;
&lt;h3 id="power-link"&gt;Power Link&lt;/h3&gt;
&lt;p&gt;"La IA provocará la inutilidad de la línea de comandos". ¿Será así? Mark Pesce —co-creador de VRML— sostiene en The Register justo lo contrario: &lt;a href="https://www.theregister.com/software/2026/03/11/ai-has-made-the-cli-more-important-and-powerful/5229986"&gt;AI has made the Command Line Interface more important and powerful than ever before&lt;/a&gt;. Su argumento es que las GUIs se volvieron tan recargadas que ni los agentes las operan bien, y la CLI reaparece como el terreno común entre humanos y máquinas. No hace falta estar de acuerdo en todo para que la pregunta quede flotando: ¿cuánto de lo que se grita en los medios y en las redes sociales vamos a tener que repensar?&lt;/p&gt;</description><category>Azure</category><category>CLI</category><category>Cockpit</category><category>GUI</category><category>sysadmin</category><guid>https://sergiobelkin.com/posts/3-power-tips-power-link-i12/</guid><pubDate>Mon, 08 Jun 2026 04:16:42 GMT</pubDate></item><item><title>Gestión térmica en notebook clamshell con RAPL y tuned</title><link>https://sergiobelkin.com/posts/gestion-termica-clamshell-rapl-tuned/</link><dc:creator>sebelk</dc:creator><description>&lt;p&gt;Tapa cerrada, monitor portátil apoyado encima, notebook como CPU box debajo de todo: lo que se llama modo &lt;em&gt;clamshell&lt;/em&gt; —la notebook cerrada como una almeja, usada solo como unidad de cómputo con monitor externo—. El objetivo: evitar que suba la temperatura dañando el hardware.&lt;/p&gt;
&lt;h3 id="el-problema-brevemente"&gt;El problema, brevemente&lt;/h3&gt;
&lt;p&gt;Los notebooks toman aire por abajo y/o expulsan calor por arriba del teclado. Un monitor portátil apoyado en clamshell rompe ese flujo. Como no se puede recuperar la disipación sin tocar hardware, lo que queda es &lt;strong&gt;bajar voluntariamente el techo de potencia&lt;/strong&gt; para que el CPU jamás llegue a temperaturas de throttling.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mejor 12W tibios sostenidos que un loop entre 28W ardiendo y 5W throttleado.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Eso es todo. El resto del post es la pelea para implementarlo en Fedora 43 (el setup no cambió en Fedora 44).&lt;/p&gt;
&lt;h3 id="las-tres-palancas"&gt;Las tres palancas&lt;/h3&gt;
&lt;p&gt;Linux expone tres puntos donde tocar, y cada uno opera en un plano distinto del sistema:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RAPL&lt;/strong&gt; (&lt;em&gt;Running Average Power Limit&lt;/em&gt;). El CPU Intel se autoimpone límites de potencia: PL1 sostenido, PL2 boost. Están en &lt;code&gt;/sys/class/powercap/intel-rapl/&lt;/code&gt;. Si los bajás, el CPU físicamente no puede consumir más. Es el techo duro, aplicado por el hardware del procesador (el PCU, &lt;em&gt;Power Control Unit&lt;/em&gt;): no depende ni del scheduler ni del usuario.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tuned&lt;/strong&gt;. Daemon de gestión de perfiles de energía. Desde Fedora 41 es el default del sistema (vía &lt;code&gt;tuned-ppd&lt;/code&gt;), reemplazando a &lt;code&gt;power-profiles-daemon&lt;/code&gt;. Maneja governor, EPP, runtime PM, sysctls, sysfs. &lt;em&gt;No baja literalmente el consumo: configura los subsistemas del kernel para que ellos lo hagan&lt;/em&gt;. Opera en el plano de las políticas, no del hardware — por eso no se solapa con RAPL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;thermald&lt;/strong&gt;. Reactivo: lee zonas térmicas y aplica medidas correctivas cuando se calienta. En este equipo nunca funcionó, y de eso también va el post.&lt;/p&gt;
&lt;h3 id="rapl-primer-round"&gt;RAPL, primer round&lt;/h3&gt;
&lt;p&gt;Bajar PL1 a 12W y PL2 a 20W es un &lt;code&gt;echo&lt;/code&gt; a un archivo en &lt;code&gt;/sys/&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tee&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;powercap&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;intel&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rapl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;constraint_0_power_limit_uw&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Tres segundos, listo. Después de verificar que el valor quedó aplicado y que las temperaturas en stress test caían a una zona razonable, vino la primera trampa con la que choco cada tanto y siempre me tomo unos minutos para acordarme:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;/sys&lt;/code&gt; no persiste. Lo que escribís ahí desaparece al reboot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Solución: un service unit propio (&lt;code&gt;rapl-limits.service&lt;/code&gt;) que reaplica los valores al boot. Heredoc, &lt;code&gt;daemon-reload&lt;/code&gt;, &lt;code&gt;enable --now&lt;/code&gt;. Hecho.&lt;/p&gt;
&lt;h3 id="thermald-el-daemon-zombi"&gt;thermald, el daemon zombi&lt;/h3&gt;
&lt;p&gt;Acá viene la parte que me costó descubrir.&lt;/p&gt;
&lt;p&gt;Habilito &lt;code&gt;thermald&lt;/code&gt;, escribo un &lt;code&gt;/etc/thermald/thermal-conf.xml&lt;/code&gt; custom, lo arranco. &lt;code&gt;systemctl status&lt;/code&gt; lo reporta activo. &lt;code&gt;ps -ef | grep thermald&lt;/code&gt; muestra el proceso vivo. Todo bien.&lt;/p&gt;
&lt;p&gt;Solo que no estaba haciendo absolutamente nada.&lt;/p&gt;
&lt;p&gt;Días después, ya con el sistema funcionando bien y con thermald deshabilitado por sospechoso, abro &lt;code&gt;journalctl -u thermald&lt;/code&gt; para confirmar la sospecha. Encuentro esto, mezclado entre los logs:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Thermal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;coretemp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sysfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;
&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Thermal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hwmon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Zones&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;present&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Need&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manually&lt;/span&gt;
&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;XML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sensor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x86_pkg_temp&lt;/span&gt;
&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Zone&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;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Una sola causa raíz, dos síntomas en el log:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No había un sensor térmico del CPU que descubrir.&lt;/strong&gt; El módulo que expone la temperatura de paquete no estaba cargado (&lt;code&gt;No coretemp sysfs found&lt;/code&gt;), así que thermald arrancó sin ninguna zona del CPU a la vista. Lo venía diciendo desde el primer boot del histórico, mucho antes de que yo empezara a tocar nada.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;El &lt;code&gt;invalid sensor type x86_pkg_temp&lt;/code&gt; no significa lo que parece.&lt;/strong&gt; &lt;code&gt;x86_pkg_temp&lt;/code&gt; es el nombre correcto del sensor de paquete; thermald no lo rechaza por schema. Resuelve el tipo en runtime contra los sensores que efectivamente descubrió, y como no había ninguno, la búsqueda volvió vacía y el bind falló. El mensaje dice "invalid type" pero quiere decir "no encontré un sensor de ese tipo en este sistema".&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Resultado: el daemon arrancaba, intentaba bindear las zonas definidas en el XML, no encontraba el sensor, y se quedaba corriendo sin nada que monitorear. Vivo, ocupando memoria, sin función.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;systemctl status&lt;/code&gt; te dice si systemd cree que el servicio está bien. No te dice si el servicio está haciendo lo que tiene que hacer. Para eso está &lt;code&gt;journalctl&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="validacion-empirica"&gt;Validación empírica&lt;/h3&gt;
&lt;p&gt;Antes de tomar la decisión final de desactivar thermald, corrí un baseline de 6 minutos midiendo Package temp y frecuencia, después un stress test de 6 minutos exactos midiendo lo mismo, y los comparé. Lo que vi fue lo que esperaba: con RAPL fijo en 12W y &lt;code&gt;tuned&lt;/code&gt; manejando el resto en su profile balanced, las temperaturas se mantenían en una zona estable y la frecuencia se sostenía sin las caídas abruptas del clamshell sin tunear.&lt;/p&gt;
&lt;p&gt;La pregunta que la medición respondía no era &lt;em&gt;"¿thermald aporta?"&lt;/em&gt; — eso ya estaba contestado de antemano por los logs. Era la otra: &lt;em&gt;"¿con solo RAPL+tuned alcanza para clamshell?"&lt;/em&gt;. La respuesta fue sí, y eso permitió cerrar el setup con tranquilidad.&lt;/p&gt;
&lt;h3 id="estado-final"&gt;Estado final&lt;/h3&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;tuned                 → active (profile balanced)
RAPL PL1=12W PL2=20W  → fijo vía rapl-limits.service
thermald              → disabled (no podía operar en este hardware)
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Filosofía: &lt;strong&gt;un techo duro de potencia (RAPL), un orquestador de estado (tuned), nada reactivo encima&lt;/strong&gt;. Tres piezas, ninguna pelea con la otra, todas debuggeables.&lt;/p&gt;
&lt;h3 id="una-nota-sobre-como-empece"&gt;Una nota sobre cómo empecé&lt;/h3&gt;
&lt;p&gt;Originalmente armé el setup con &lt;code&gt;tlp&lt;/code&gt; + RAPL, siguiendo una sugerencia de Claude (vía claude.ai) que asumía &lt;code&gt;power-profiles-daemon&lt;/code&gt; como el daemon por defecto en Fedora. No contrasté la premisa. Cuando fui a verificarla, descubrí que desde Fedora 41 el default pasó a &lt;code&gt;tuned&lt;/code&gt; (vía &lt;code&gt;tuned-ppd&lt;/code&gt;), no PPD. Y para este escenario — clamshell estático, siempre AC, sin diferenciar AC/BAT, sin que &lt;code&gt;tlp-rdw&lt;/code&gt; aporte nada — &lt;code&gt;tlp&lt;/code&gt; no resolvía nada que &lt;code&gt;tuned&lt;/code&gt; no resuelva. Migré. El post documenta el setup resultante, no el camino original.&lt;/p&gt;
&lt;p&gt;La lección no es "no preguntarle a una IA". Es &lt;strong&gt;verificar la premisa que la IA está usando, no solo la recomendación que te entrega&lt;/strong&gt;. Un asistente puede recomendar una herramienta correctamente &lt;em&gt;condicional a&lt;/em&gt; un default que ya no es el actual; sin contrastar esa premisa, terminás con un setup más complicado del necesario.&lt;/p&gt;
&lt;h3 id="lo-que-me-llevo"&gt;Lo que me llevo&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Un daemon puede estar "running" y no estar haciendo nada. La verdad está en los logs de aplicación, no en &lt;code&gt;systemctl status&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Un unit file propio de pocas líneas a veces es la decisión correcta y que no requiere demasiado esfuerzo.&lt;/li&gt;
&lt;li&gt;El consejo técnico de una IA puede ser internamente coherente y aún así basarse en premisas obsoletas (defaults, versiones, comandos). El costo de chequear la premisa antes de implementar es bajo; el costo de no hacerlo se paga cada vez que volvés a tocar el equipo.&lt;/li&gt;
&lt;/ul&gt;</description><category>monitoreo</category><category>power-management</category><category>sysadmin</category><guid>https://sergiobelkin.com/posts/gestion-termica-clamshell-rapl-tuned/</guid><pubDate>Sat, 30 May 2026 22:56:08 GMT</pubDate></item><item><title>3 Power Tips + Power Link I11</title><link>https://sergiobelkin.com/posts/3-power-tips-power-link-i11/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/PowerTipsPlus.png"&gt;&lt;/figure&gt; &lt;p&gt;Las PTs de este issue muestran cómo, incluso en la nube (en este caso Azure), siguen aplicando conceptos clásicos de sistemas: distinguir entre ejecución y estado, entender la irreversibilidad de ciertos valores (como secrets) y operar en ausencia de un mapeo explícito entre componentes. Al final, el progreso en la &lt;strong&gt;Estrategia de Innovación Abierta y Código Abierto&lt;/strong&gt; en un estado alemán.&lt;/p&gt;
&lt;h3 id="power-tip-1-el-exit-code-es-local-no-de-azure"&gt;Power Tip #1 El exit code es local, no de Azure&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;El comando volvió con &lt;code&gt;0&lt;/code&gt; y la app sigue cayéndose.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;az&lt;/code&gt; reporta éxito al recibir el ACK del control plane.&lt;/li&gt;
&lt;li&gt;El recurso queda en &lt;code&gt;Updating&lt;/code&gt; o &lt;code&gt;Creating&lt;/code&gt; por minutos.&lt;/li&gt;
&lt;li&gt;El siguiente paso del script lo encuentra "no listo" y falla raro.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;az&lt;span class="w"&gt; &lt;/span&gt;resource&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;--ids&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--query&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"properties.provisioningState"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;tsv
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;provisioningState&lt;/code&gt; es un testigo más confiable del control plane.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="power-tip-2-el-valor-del-secret-se-entrega-una-sola-vez"&gt;Power Tip #2 El valor del secret se entrega una sola vez&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;La alerta de vencimiento de un secret se "resolvió" pero la app igual se cae.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;credential reset&lt;/code&gt; devolvió el password en stdout.&lt;/li&gt;
&lt;li&gt;Lo perdiste antes de guardarlo en el vault.&lt;/li&gt;
&lt;li&gt;Entra ID considera el secret válido. Nadie puede usarlo.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;az&lt;span class="w"&gt; &lt;/span&gt;ad&lt;span class="w"&gt; &lt;/span&gt;app&lt;span class="w"&gt; &lt;/span&gt;credential&lt;span class="w"&gt; &lt;/span&gt;reset&lt;span class="w"&gt; &lt;/span&gt;--append&lt;span class="w"&gt; &lt;/span&gt;...&lt;span class="w"&gt; &lt;/span&gt;--query&lt;span class="w"&gt; &lt;/span&gt;password&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;tsv
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Si se perdió el valor generado, no se puede recuperar. Como perder una clave privada SSH: podés crear otra, pero no reconstruir la anterior.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="power-tip-3-el-mapeo-no-vive-donde-uno-espera"&gt;Power Tip #3 El mapeo no vive donde uno espera&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Sabés que la app usa un secret de un KV. No sabés cuál.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App Registration no declara qué KV la consume.&lt;/li&gt;
&lt;li&gt;Key Vault no mantiene, por defecto, una relación nativa con la App Registration que originó el secret.&lt;/li&gt;
&lt;li&gt;El nombre del secret en KV es convención del equipo responsable, no metadato de Azure.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;az&lt;span class="w"&gt; &lt;/span&gt;keyvault&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;--query&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[].name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;tsv&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;az&lt;span class="w"&gt; &lt;/span&gt;keyvault&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;--vault-name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$kv&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--query&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[?contains(name, 'mi-superpower-app')].{kv:'&lt;/span&gt;&lt;span class="nv"&gt;$kv&lt;/span&gt;&lt;span class="s2"&gt;',name:name}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;tsv
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Si para rotar un secret tenés que salir a buscarlo, no está definido en ningún lado qué secret usa esa app...&lt;/p&gt;
&lt;h3 id="power-link"&gt;Power Link&lt;/h3&gt;
&lt;p&gt;Este es un análisis del &lt;strong&gt;Open Source Observatory (OSOR)&lt;/strong&gt; sobre la adopción de software Open Source en el estado alemán de Schleswig-Holstein. ¿Cómo les fue implementando LibreOffice, Thunderbird, and Linux?: &lt;a href="https://interoperable-europe.ec.europa.eu/collection/open-source-observatory-osor/news/schleswig-holsteins-open-source-strategy-year"&gt;From Early Adopter to Leader: Schleswig-Holstein's Open Source Evolution&lt;/a&gt;&lt;/p&gt;</description><category>Azure</category><category>seguridad</category><guid>https://sergiobelkin.com/posts/3-power-tips-power-link-i11/</guid><pubDate>Mon, 27 Apr 2026 16:57:09 GMT</pubDate></item><item><title>3 Power Tips + Power Link I10</title><link>https://sergiobelkin.com/posts/3-power-tips-power-link-i10/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/PowerTipsPlus.png"&gt;&lt;/figure&gt; &lt;p&gt;Las herramientas de &lt;strong&gt;monitoreo&lt;/strong&gt; son fundamentales, pero una pobre &lt;strong&gt;interpretación&lt;/strong&gt; de los resultados, pueden llevar a conclusiones incorrectas. Incluso usando chatbots de IA, si hacemos &lt;strong&gt;preguntas&lt;/strong&gt; deficientes podemos terminar girando en círculos o tardar más tiempo en resolver un problema. Lo mejor es reducir el nivel de especulación con hipótesis técnica. A continuación vemos unos ejemplos bien sencillos.&lt;/p&gt;
&lt;h3 id="power-tip-1"&gt;Power Tip #1&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;No todas las métricas que parecen similares describen el mismo fenómeno.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Aplicación lenta.&lt;/li&gt;
&lt;li&gt;Load 15.&lt;/li&gt;
&lt;li&gt;CPU idle 50%.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Alguien descarta CPU porque “está libre”.&lt;/p&gt;
&lt;p&gt;Pero:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Load describe tareas en ejecución o esperando.&lt;/li&gt;
&lt;li&gt;CPU usage describe tiempo ocupado.&lt;/li&gt;
&lt;li&gt;CPU pressure describe tiempo en que las tareas no pudieron progresar.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Son fenómenos distintos.&lt;/p&gt;
&lt;p&gt;Antes de interpretar, mirá comportamiento:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;vmstat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
mpstat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Y si necesitás otra capa:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/proc/pressure/cpu
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Es importante saber que “Load no es necesariamente consumo de CPU”, sin embargo, más importante es entender qué está describiendo cada señal.&lt;/p&gt;
&lt;p&gt;Interpretarlas como si hablaran de lo mismo es especulación con números.&lt;/p&gt;
&lt;h3 id="power-tip-2"&gt;Power Tip #2&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Tomar una métrica aislada de memoria en Linux no implica entender como funciona&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;free&lt;span class="w"&gt; &lt;/span&gt;-h
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Las columnas están ahí. Pero entender qué representa cada una es otra cosa.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;used&lt;/code&gt; no significa “memoria en crisis”.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;buff/cache&lt;/code&gt; no significa “desperdicio”.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;available&lt;/code&gt; no significa “memoria libre inmediata”.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Antes de reaccionar ante un número alto, mirá dinámica:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;vmstat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Linux administra memoria como recurso dinámico, no como espacio estático.&lt;/p&gt;
&lt;p&gt;Leer columnas sin entender el modelo del kernel es especulación con formato tabular.&lt;/p&gt;
&lt;h3 id="power-tip-3"&gt;Power Tip #3&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Reiniciar en muchas situaciones no hace otra cosa que restaurar un estado. No explica causas.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;El servicio falla.&lt;/li&gt;
&lt;li&gt;Se reinicia.&lt;/li&gt;
&lt;li&gt;Funciona.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eso no es diagnóstico.&lt;/p&gt;
&lt;p&gt;Antes de repetir el gesto automático:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;journalctl&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;servicio-problematico&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Si no entendemos por qué se degradó, volverá a pasar.&lt;/p&gt;
&lt;p&gt;Reiniciar elimina el síntoma. No valida la hipótesis. Las herramientas no están equivocadas.&lt;/p&gt;
&lt;p&gt;La diferencia está en entender qué fenómeno describe cada señal. Está claro que en el mundo real, muchas veces determinar la causa del problema lleva más tiempo que usar un trigger que reinice un servicio. Algo que pasa muchas veces apps legacy (la falta de especialistas o la triste realidad de falta del código fuente). Sin embargo, el problema es cuando se usa esta metodologìa como primera opción...&lt;/p&gt;
&lt;h3 id="power-link"&gt;Power Link&lt;/h3&gt;
&lt;p&gt;La IA terminará con las operaciones de IT. ¿En serio? Bueno, en realidad no, para sorpresa de tecno-optimistas o tecno-pesimistas, aquí hay un interesante análisis de Sebastián Martínez de SUSE sobre este tema: &lt;a href="https://www.suse.com/c/why-ai-cannot-run-linux-infrastructure-mcp-suse/"&gt;Why AI Still Cannot Run Your Linux Infrastructure (And What Must Change)&lt;/a&gt;&lt;/p&gt;</description><category>kernel</category><category>monitoreo</category><category>RAM</category><category>systemd</category><guid>https://sergiobelkin.com/posts/3-power-tips-power-link-i10/</guid><pubDate>Sat, 21 Feb 2026 16:57:09 GMT</pubDate></item><item><title>3 Power Tips + 1 Power Link I9</title><link>https://sergiobelkin.com/posts/3-power-tips-power-link-i9/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/PowerTipsPlus.png"&gt;&lt;/figure&gt; &lt;p&gt;&lt;strong&gt;PSI (Pressure Stall Information)&lt;/strong&gt; permite observar ese tipo de situaciones y, sobre todo, cambiar la forma en que interpretamos problemas de rendimiento y estabilidad en Linux. A partir de esa señal, el foco deja de estar en métricas globales y pasa a decisiones concretas: entender dónde ocurre el problema, a quién afecta y cómo intervenir sin arrastrar todo el sistema.
Finalmente, el Power Link nos lleva a ver qué opina Gaël Duval, el creador de la distribución Mandrake, sobre uno de los hypes actuales.&lt;/p&gt;
&lt;h3 id="power-tip-1-como-se-ven-afectadas-mis-aplicaciones-por-falta-de-recursos"&gt;Power Tip #1: ¿Cómo se ven afectadas mis aplicaciones por falta de recursos?&lt;/h3&gt;
&lt;p&gt;Existe a veces una creencia basado en pensamiento mágico: que el monitoreo depende solamente de la herramienta que se utilice. Pero más que la herramienta lo importante, es &lt;em&gt;qué se mide&lt;/em&gt; y &lt;em&gt;cómo interpretar&lt;/em&gt; esos resultados. &lt;strong&gt;Las métricas más conocidas: uso de cpu, load-average, uso de memoria, uso de I/O, etc. si bien son útiles pueden darnos en muchos casos un panorama parcial&lt;/strong&gt;. Eso sucede porque están basados en los recursos, o en el scheduler.&lt;/p&gt;
&lt;p&gt;Existen sin embargo desde hace varios años métricas que están más centradas en como las aplicaciones se ven impactadas por el uso de recursos.  Con PSI (Pressure Stall Information) obtenemos un indicio del tiempo que las aplicaciones quedan detenidas porque el sistema no puede darles CPU, memoria o acceso a disco en el momento en que lo necesitan. &lt;/p&gt;
&lt;p&gt;Esto está expuesto en el sistema de archivos:&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/psi-files.webp"&gt;&lt;img src="https://sergiobelkin.com/images/psi-files.thumbnail.webp" alt="Archivos de PSI"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;PSI distingue entre situaciones donde al menos una tarea queda bloqueada (&lt;code&gt;some&lt;/code&gt;) y escenarios donde todas las tareas relevantes compiten por el mismo recurso (&lt;code&gt;full&lt;/code&gt;).
Valores elevados y sostenidos en este &lt;code&gt;full&lt;/code&gt; caso suelen indicar un problema estructural: no de picos puntuales, sino de diseño, aislamiento o priorización de workloads.&lt;/p&gt;
&lt;p&gt;Vale hacer aquí tres aclaraciones:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Esta funcionalidad está presente a partir de la versión 4.20 del kernel (RHEL8 la trae como backport). En algunas distribuciones puede ser necesario habilitar PSI explícitamente en el arranque.&lt;/li&gt;
&lt;li&gt;Esta no es &lt;strong&gt;la&lt;/strong&gt; métrica definitiva, sin embargo ignorarla es perder el cuadro completo de lo que sucede tanto en el sistema como nuestras aplicaciones.&lt;/li&gt;
&lt;li&gt;Versiones recientes del kernel incluyen también la medición de la presión sobre &lt;strong&gt;irq&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="power-tip-2-las-disrupciones-se-pueden-atribuir-a-workloads-especificos"&gt;Power Tip #2: Las disrupciones se pueden atribuir a workloads específicos&lt;/h3&gt;
&lt;p&gt;Cuando el host empieza a perder capacidad de respuesta, no siempre el problema es global.
Es común que el sistema siga siendo usable mientras algunos servicios o aplicaciones comienzan a degradarse, simplemente porque no acceden a los recursos cuando los necesitan. Esa degradación suele manifestarse como lentitud, mayor latencia o pérdida de capacidad de interacción.&lt;/p&gt;
&lt;p&gt;En un Linux moderno, el kernel organiza los procesos bajo &lt;strong&gt;dominios de control definidos por cgroups&lt;/strong&gt;.
Esto permite observar de forma localizada qué tareas empiezan a quedarse esperando recursos, en lugar de razonar únicamente a partir de métricas agregadas del host.&lt;/p&gt;
&lt;p&gt;En sistemas basados en systemd, ese dominio no es abstracto: se corresponde con services, slices o scopes.
El mismo síntoma que aparece a nivel global puede observarse dentro de estos límites, lo que permite atribuir dónde se generan las esperas, incluso cuando el resto del sistema continúa funcionando con normalidad.&lt;/p&gt;
&lt;p&gt;Pensar a nivel host diluye el diagnóstico.
En cambio poner el foco en en services, scopes y slices permite relacionar directamente los síntomas percibidos —lentitud, tiempos de respuesta, degradación de rendimiento— con un grupo de procesos concreto.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/specific-psi.webp"&gt;&lt;img src="https://sergiobelkin.com/images/specific-psi.thumbnail.webp" alt="PSI per cgroup"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Es decir, uno puede decir que &lt;strong&gt;el host muestra la presión acumulada&lt;/strong&gt;, pero &lt;strong&gt;los cgroups permiten entender qué se queda atascado y frente qué recurso&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="power-tip-3-abordar-el-problema-de-manera-localizada"&gt;Power Tip #3: Abordar el problema de manera localizada&lt;/h3&gt;
&lt;p&gt;Cuando un servicio o aplicación empieza a tener tiempos muertos o a interferir con el resto, tenemos varias maneras de abordar el problema. Y no estamos hablando de las típicas acciones: reiniciarlo, moverlo o rediseñarlo.&lt;/p&gt;
&lt;p&gt;Podemos tomar medidas más estructurales, por ejemplo, crear un slice de systemd, establecer límites y luego confinar esos servicios o procesos en esos slices.&lt;/p&gt;
&lt;p&gt;Sin embargo, antes de intervenir de manera prematura, es conveniente tener en cuenta que Linux, en general ya tiene una agrupación de slices, scopes y servicios bastante razonable. En este punto entonces, ya sabemos que &lt;strong&gt;ese servicio problemático ya corre dentro de un perímetro definido&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;En lugar de crear un nuevo slice, que tiene un carácter más permanente, podemos adoptar una solución más sencilla y gradualista. En systemd, &lt;strong&gt;los parámetros que gobiernan cómo compite por recursos no están fijos&lt;/strong&gt; en scopes o servicios.&lt;/p&gt;
&lt;p&gt;Un vistazo rápido alcanza para verlo (los siguientes son ejemplos para entender la idea):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;ControlGroup,CPUWeight,MemoryMax,IOWeight&lt;span class="w"&gt; &lt;/span&gt;procesos-ruidosos.scope
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Y tomar cartas en el asunto:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;set-property&lt;span class="w"&gt; &lt;/span&gt;procesos-ruidosos.scope&lt;span class="w"&gt; &lt;/span&gt;…
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ese tipo de intervención es local, reversible y no convierte un incidente puntual en un cambio estructural.&lt;/p&gt;
&lt;p&gt;Intervenir sobre una unidad existente permite corregir interferencias concretas &lt;strong&gt;sin rediseñar el sistema ni propagar el problema&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="fuentes-y-mas-recursos"&gt;Fuentes y más recursos&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://sergiobelkin.com/posts/que-son-los-cgroups-y-para-que-sirven/"&gt;Qué son los cgroups y para qué sirven&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="power-link"&gt;Power Link&lt;/h3&gt;
&lt;p&gt;Gaël Duval, creador de Mandrake, Murena y e/OS reflexiona sobre el impacto de la AI en el software open source: &lt;a href="https://gaelduval.com/why-ai-wont-kill-open-source/#more-1725"&gt;Why AI won’t “Kill Open Source”&lt;/a&gt;&lt;/p&gt;</description><category>cgroups</category><category>kernel</category><category>monitoreo</category><category>systemd</category><guid>https://sergiobelkin.com/posts/3-power-tips-power-link-i9/</guid><pubDate>Sun, 04 Jan 2026 18:30:54 GMT</pubDate></item><item><title>Plan Táctico y Estratégico de la Memoria en Linux</title><link>https://sergiobelkin.com/posts/plan-tactico-y-estrategico-de-la-memoria-en-linux/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/sk-CNBRg1K9QvQ-unsplash.jpg"&gt;&lt;/figure&gt; &lt;p&gt;La mitología en torno a la memoria en Linux ha producido una serie de relatos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;En un extremo: Linux puede funcionar con muy poca memoria RAM.&lt;/li&gt;
&lt;li&gt;Del otro lado: Linux consume mucha memoria.&lt;/li&gt;
&lt;li&gt;Y que una partición swap debe tener entre 1 a 2 veces la memoria RAM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Como vemos, algunas historias son más recientes, otras más antiguas, pueden ser parcialmente ciertas y hasta contradictorias entre sí.&lt;/p&gt;
&lt;p&gt;Este artículo tiene como propósitos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explicar de manera sencilla el funcionamiento de la memoria en Linux, desmitificando también algunos conceptos.&lt;/li&gt;
&lt;li&gt;Enumerar y describir tácticas para que el uso de la memoria proporcione la mejor usabilidad y experiencia del usuario.&lt;/li&gt;
&lt;li&gt;Ofrecer alternativas para que cada uno elija la mejor opción de acuerdo a sus necesidades.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="definiciones"&gt;Definiciones&lt;/h3&gt;
&lt;p&gt;Vamos a repasar algunos conceptos básicos que de manera más o menos frecuente usamos, usaremos metáforas en el camino. Ninguna metáfora es perfecta, sin embargo ellas nos ayudan a entender la realidad.&lt;/p&gt;
&lt;h4 id="memoria-virtual"&gt;Memoria Virtual&lt;/h4&gt;
&lt;p&gt;La memoria virtual es el mecanismo por el cual cada proceso recibe un espacio de direcciones propio, independiente y protegido. El hardware traduce direcciones virtuales a físicas según tablas de páginas. Esto permite aislamiento, sobreasignación, mapeos de archivos y demanda dinámica de páginas, más allá de la cantidad de RAM disponible. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux trata de usar la mayor cantidad de memoria posible, para poder ejecutar las aplicaciones y acceder a los archivos de la manera más rápida posible. De manera que si la memoria libre es baja no es necesariamente un indicativo de un problema.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="page"&gt;Page&lt;/h4&gt;
&lt;p&gt;Una página es la unidad mínima de memoria, de tamaño fijo (típicamente 4 KB), que el kernel y el hardware de gestión de memoria del procesador utilizan para organizar el espacio de direcciones y realizar el mapeo entre direcciones virtuales y físicas.&lt;/p&gt;
&lt;h4 id="page-table"&gt;Page table&lt;/h4&gt;
&lt;p&gt;Es una estructura de datos jerárquica administrada por el kernel y usada por el hardware para traducir direcciones virtuales a direcciones físicas, con información de permisos y estado de cada página.&lt;/p&gt;
&lt;h4 id="page-fault"&gt;Page Fault&lt;/h4&gt;
&lt;p&gt;Un &lt;strong&gt;page fault&lt;/strong&gt; ocurre cuando un proceso accede a una dirección válida de su espacio de memoria pero la página correspondiente no está preparada para ese acceso. Puede deberse a que la página aún no fue cargada, a que debe asignarse, o a que debe traerse desde disco. Si la página no está en RAM, el kernel debe cargarla, lo que puede ser lento; si ya estaba en RAM, el costo es menor.&lt;/p&gt;
&lt;h4 id="page-cache"&gt;Page cache&lt;/h4&gt;
&lt;p&gt;Es la caché de páginas de archivos gestionada por el kernel, usada para acelerar lecturas y escrituras almacenando en RAM los datos y metadatos de archivos y directorios, reduciendo accesos al disco.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/cache.png"&gt;&lt;img src="https://sergiobelkin.com/images/cache.thumbnail.png" alt="Page cache"&gt;&lt;/a&gt;  &lt;/p&gt;
&lt;h4 id="tipos-de-memoria"&gt;Tipos de memoria&lt;/h4&gt;
&lt;h5 id="file-memory"&gt;File Memory&lt;/h5&gt;
&lt;p&gt;Es la memoria relacionada con el &lt;strong&gt;Page Cache&lt;/strong&gt;.&lt;/p&gt;
&lt;h5 id="anonymous-memory"&gt;Anonymous Memory&lt;/h5&gt;
&lt;p&gt;Es la memoria que un proceso usa y que no está respaldada por un archivo: heap (asignada dinámicamente), stack (llamadas a funciones y almacenamiento de variables locales), COW (parte de la memoria cuando se crea un proceso hijo) y mapeos con MAP_ANONYMOUS. Su único respaldo posible es la swap.
La memoria anónima se crea y utiliza en RAM. Si el kernel necesita expulsarla de RAM, su único destino posible es la swap, porque no tiene un archivo desde el cual reconstruirse. Pero su existencia normal no depende de la swap.&lt;/p&gt;
&lt;h4 id="memory-pressure"&gt;Memory Pressure&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Memory pressure&lt;/strong&gt; es un estado en el que las páginas libres caen por debajo de umbrales críticos, obligando al kernel a iniciar mecanismos de liberación de memoria.&lt;/p&gt;
&lt;p&gt;En términos prácticos cuando la presión es alta pueden surgir ciertos síntomas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sitios web / servidores HTTP:&lt;/strong&gt;
  La latencia aumenta porque los workers deben esperar a que el kernel libere páginas. Además, puede haber más CPU gastada en recuperar memoria, lo que degrada aún más los tiempos de respuesta.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sistemas de escritorio:&lt;/strong&gt;
  El sistema pierde fluidez porque el scheduler empieza a verse afectado por stalls debidos a las operaciones para liberar memoria y, si hay swap, el sistema puede entrar en &lt;em&gt;swap thrashing&lt;/em&gt;. Esto produce lag del mouse, ventanas que tardan en responder, escritorios congelados por segundos, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Acceso remoto (SSH, RDP, VNC):&lt;/strong&gt;
  Al haber presión, las operaciones de usuario-espacio tardan más en ejecutarse, y los daemons pueden quedar brevemente estancados esperando memoria. Esto causa retrasos notables en la interacción remota.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;¿Y qué sucede si la presión de memoria puede llegar a ser tan alta que el kernel ya no logra conseguir memoria ni siquiera después de intentar liberarla?&lt;/p&gt;
&lt;h4 id="thrashing"&gt;Thrashing&lt;/h4&gt;
&lt;p&gt;El &lt;strong&gt;thrashing&lt;/strong&gt; ocurre cuando la memoria RAM no alcanza para mantener las páginas que los procesos usan todo el tiempo. El kernel empieza a sacar páginas de memoria para hacer lugar a otras nuevas, pero enseguida vuelve a necesitarlas. Esto genera un bucle de fallos de página y de recarga constante desde el disco.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Con swap:&lt;/strong&gt; las páginas anónimas van y vienen entre RAM y swap, lo que dispara el uso de disco y vuelve el sistema extremadamente lento.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sin swap:&lt;/strong&gt; las páginas anónimas no tienen adónde ir y el kernel termina activando el &lt;strong&gt;OOM killer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incluso sin swap:&lt;/strong&gt; puede haber thrashing si las páginas vienen de archivos (page cache), ya que el kernel debe recargarlas una y otra vez.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="oom-out-of-memory"&gt;OOM (Out-Of-Memory) 💀 🔥&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Out-Of-Memory&lt;/strong&gt; es una situación en la cual el kernel agotó todos los mecanismos para liberar memoria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Liberar páginas del caché.&lt;/li&gt;
&lt;li&gt;Mover páginas anónimas a la swap.&lt;/li&gt;
&lt;li&gt;Compactar memoria.&lt;/li&gt;
&lt;li&gt;Liberar memoria mediante &lt;code&gt;kswapd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Aplicación de políticas de cgroups (muy común al usar contenedores).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="oom-killer"&gt;OOM killer&lt;/h4&gt;
&lt;p&gt;El &lt;strong&gt;OOM killer&lt;/strong&gt; es el mecanismo que usa el kernel cuando ya no puede asignar más memoria, incluso después de intentar liberar todas las páginas que es posible descartar o mover fuera de la RAM.
En esa situación crítica, el kernel calcula un puntaje para cada proceso (&lt;code&gt;oom_score&lt;/code&gt;) y finaliza al que resulte más conveniente para recuperar memoria rápidamente y permitir que el sistema siga funcionando.
Este comportamiento puede influenciarse ajustando &lt;code&gt;oom_score_adj&lt;/code&gt;, que hace que un proceso sea más o menos propenso a ser elegido.&lt;/p&gt;
&lt;h4 id="oom_score"&gt;oom_score&lt;/h4&gt;
&lt;p&gt;Como ya se mencionó a cada proceso se le asigna un puntaje de acuerdo a distintos factores, cuanto más alto es, más susceptible es a ser terminado por OOM killer. &lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/oom_score-from-proc.webp"&gt;&lt;img src="https://sergiobelkin.com/images/oom_score-from-proc.thumbnail.webp" alt="OOM Scores por proceso en /proc" title="Clic para agrandar"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Y también, como dijimos mediante &lt;code&gt;oom_score_adj&lt;/code&gt; podemos influir en el score de un proceso:&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/oom_score-adjfrom-proc.webp"&gt;&lt;img src="https://sergiobelkin.com/images/oom_score-adjfrom-proc.thumbnail.webp" alt="Incidencia en oom_score mediante oom_score_adj"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;En versiones más recientes de las distribuciones existe el comando &lt;code&gt;choom&lt;/code&gt; que permite ver y/o ajustar dicho valor.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/choom.png"&gt;&lt;img src="https://sergiobelkin.com/images/choom.thumbnail.png" alt="choom"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h4 id="swap"&gt;Swap&lt;/h4&gt;
&lt;p&gt;Los usuarios ocasionales de Linux y aun muchos sysadmins tienen una idea negativa sobre "la swap". Simplificaciones extremas y conceptos anticuados la han convertido en la gran villana de la historia del sistema operativo.&lt;/p&gt;
&lt;p&gt;Si comparamos a la memoria con un escritorio, sin swap podría lucir así:&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/ashim-d-silva-Kw_zQBAChws-unsplash.jpg"&gt;&lt;img src="https://sergiobelkin.com/images/ashim-d-silva-Kw_zQBAChws-unsplash.jpg" alt="Prescindir de swap no es una opción sana."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@randomlies?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Ashim D’Silva&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/mess?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Así que primero vamos a decir lo que &lt;strong&gt;no&lt;/strong&gt; es:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No&lt;/strong&gt; es la &lt;strong&gt;memoria virtual&lt;/strong&gt; sino que &lt;strong&gt;forma parte&lt;/strong&gt; de la &lt;strong&gt;técnica&lt;/strong&gt; que realiza el sistema operativo para administrar la memoria.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No&lt;/strong&gt; es un espacio de reserva ni un último recurso. &lt;strong&gt;Es&lt;/strong&gt; un espacio complementario que sirve para liberar RAM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No&lt;/strong&gt; funciona como último recurso, pero el sistema operativo puede mover hacia la swap las páginas de memoria no usadas recientemente.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No&lt;/strong&gt; es algo de lo que el sistema operativo pueda prescindir alegremente, aun cuando la cantidad de memoria RAM física sea grande. Quienes parecen haber inventado la pólvora, nos cuentan que es posible técnicamente que Linux funcione sin swap. Si bien es cierto, al carecer de swap, el kernel no tiene manera de mover páginas de memoria anónimas hacia el área de swap y liberar así RAM para procesos que la necesitan urgentemente.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nuestro escritorio con swap:&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/alexandru-acea-Zg9R__O-8fM-unsplash.jpg"&gt;&lt;img src="https://sergiobelkin.com/images/alexandru-acea-Zg9R__O-8fM-unsplash.thumbnail.jpg" alt="Analogía de Swap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@alexacea?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Alexandru Acea&lt;/a&gt; (edited by me) on &lt;a href="https://unsplash.com/s/photos/desktop-drawer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;¿Los cajones de un escritorio los usamos cuando lo tenemos abarrotado de cosas? No, los usamos para guardar cosas que no son de alta prioridad. Aunque es cierto, si luego queremos usar esa tijera o aquel destornillador en algún momento requerirá un poco más de trabajo, tendremos que abrir el cajón, buscarlo, extraerlo, etc.&lt;/p&gt;
&lt;p&gt;Ah, y la swap también sirve para hibernar, aunque honestamente no sé cuánta gente mantiene esa práctica.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="tuning"&gt;Tuning&lt;/h3&gt;
&lt;p&gt;Ahora veremos diferentes tácticas que podemos usar para optimizar el uso de la memoria.&lt;/p&gt;
&lt;h4 id="cgroupv2"&gt;cgroupv2&lt;/h4&gt;
&lt;p&gt;cgroup es un mecanismo para organizar los procesos de manera jerárquica y distribuir los recursos del sistema a lo largo de la jerarquía en una manera controlada y configurada.&lt;/p&gt;
&lt;p&gt;Un cgroup se compone de un núcleo que es responsable primariamente de organizar de manera jerárquica los procesos y controladores que comúnmente distribuyen un tipo específico de recurso del sistema a lo largo de la jerarquía.&lt;/p&gt;
&lt;p&gt;En la versión 2 de cgroup un proceso no puede pertenecer a diferentes grupos para diferentes controladores. Si el proceso se une al grupo alfa, todos los controladores para alfa tomarán control de ese proceso.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/ps-cgroup.png"&gt;&lt;img src="https://sergiobelkin.com/images/ps-cgroup.thumbnail.png" alt="ps mostrando cgroup"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Supongamos que queremos que los procesos de un cgroup (y sus grupos hijos) conserven su memoria y no sean los primeros en perderla cuando el sistema necesita liberar RAM. Para eso sirve el parámetro &lt;strong&gt;memory.low&lt;/strong&gt;: mientras el uso de memoria del cgroup se mantenga por debajo de ese valor, el kernel evita quitarle páginas y prefiere reclamarlas de los otros cgroups que no están protegidos. Recién cuando ya no queda nada por reclamar en otro lado toca la memoria protegida.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/memory_low.png"&gt;&lt;img src="https://sergiobelkin.com/images/memory_low.thumbnail.png" alt="el parámetro memory.low"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Otro parámetro interesante para monitorear es &lt;strong&gt;memory.pressure&lt;/strong&gt;. Tiene dos líneas, &lt;code&gt;some&lt;/code&gt; y &lt;code&gt;full&lt;/code&gt;, que registran cuánto tiempo hubo tareas demoradas por falta de memoria. Entonces si miramos el archivo &lt;code&gt;/sys/fs/cgroup/user.slice/memory.pressure&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;some avg10=0.00 avg60=0.13 avg300=0.12 total=1690238
full avg10=0.00 avg60=0.10 avg300=0.09 total=1394199
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Significa que, dentro del grupo de control user.slice, en los últimos 10 segundos no hubo tareas afectadas por presión de memoria. Sin embargo, en el último minuto las tareas estuvieron bloqueadas un 0,13% del tiempo y un 0,12% en los últimos cinco minutos. El valor total indica que estas situaciones acumulan aproximadamente 1,7 segundos de espera.
La segunda línea muestra las mismas métricas, pero aplicadas a los casos en que todas las tareas del grupo estuvieron simultáneamente afectadas por presión de memoria (full en lugar de some).&lt;/p&gt;
&lt;p&gt;Es decir:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;some&lt;/code&gt; → indica si un retraso afectó al menos una tarea.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;full&lt;/code&gt; → indica si el retraso afectó a todo el cgroup simultáneamente.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="zram"&gt;zram&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;zram&lt;/strong&gt; es por así decirlo, una manera cool de usar swap gracias a un módulo del kernel. 
&lt;br&gt;
&lt;a class="image-reference" href="https://sergiobelkin.com/images/chuttersnap-4LnSe9KwewA-unsplash.jpg"&gt;&lt;img src="https://sergiobelkin.com/images/chuttersnap-4LnSe9KwewA-unsplash.thumbnail.jpg" alt="zram, swap pero cool"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@chuttersnap?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;chuttersnap&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/cool-drawer-desktop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;En lugar de gastar espacio en un disco (sea rígido o sólido) usamos dispositivos de bloque en la propia RAM. Los bloques swapeados se guardan comprimidos. Estos discos virtuales son rápidos y ahorran memoria.&lt;/p&gt;
&lt;p&gt;Una de las pocas desventajas que tiene esta metodología es la incapacidad para poder hibernar el sistema operativo, al no estar presente la partición en un almacenamiento de tipo persistente.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/zram.png"&gt;&lt;img src="https://sergiobelkin.com/images/zram.png" alt="zram"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h4 id="earlyoom"&gt;EarlyOOM&lt;/h4&gt;
&lt;p&gt;El OOM killer del kernel solamente se dispara en situaciones extremas y le puede llevar mucho tiempo hasta que puede enviar SIGKILL a los procesos que sean necesarios para poder liberar memoria. Durante ese tiempo probablemente el usuario no pueda interactuar con el sistema operativo.&lt;/p&gt;
&lt;p&gt;EarlyOOM trabaja en espacio de usuario y por lo tanto se puede anticipar y ser mucho más rápido.&lt;/p&gt;
&lt;p&gt;El comportamiento predeterminado en Fedora es que si tanto la RAM como la swap libre caen por debajo del 10%, EarlyOOM le envía una señal de terminación al proceso con el &lt;code&gt;oom_score&lt;/code&gt; más alto. Si la RAM como swap libre bajan por debajo del 5%, EarlyOOM le enviará una señal para matar a ese proceso, el de &lt;code&gt;oom_score&lt;/code&gt; más elevado.&lt;/p&gt;
&lt;p&gt;La idea es recuperar la usabilidad (especialmente en un entorno de escritorio) lo antes posible.&lt;/p&gt;
&lt;p&gt;El problema es que EarlyOOM no soporta al momento la medición de la &lt;strong&gt;memory pressure&lt;/strong&gt; como indicativo para tomar decisiones.&lt;/p&gt;
&lt;h4 id="nohang"&gt;nohang&lt;/h4&gt;
&lt;p&gt;Este servicio es mucho más configurable y aporta una mejor solución que EarlyOOM.&lt;/p&gt;
&lt;p&gt;Algunas funcionalidades son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Se puede elegir la acción que realizará en una situación OOM.&lt;/li&gt;
&lt;li&gt;Ofrece varios criterios para elegir los procesos a finalizar.&lt;/li&gt;
&lt;li&gt;Soporta zram&lt;/li&gt;
&lt;li&gt;Puede usar &lt;strong&gt;memory pressure&lt;/strong&gt; para tomar una acción.&lt;/li&gt;
&lt;li&gt;El archivo de configuración es medianamente sencillo&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sin embargo, este proyecto en la actualidad tiene poca actividad comparado por ejemplo con EarlyOOM&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/nohangvseoom.webp"&gt;&lt;img src="https://sergiobelkin.com/images/nohangvseoom.thumbnail.webp" alt="Repos: nohang vs EarlyOOM en Github"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h4 id="zswap"&gt;zswap&lt;/h4&gt;
&lt;p&gt;Con zswap no reemplazamos el espacio swap en el disco sino que usamos un caché comprimido en la RAM. Este método ahorra I/O, obteniendo entonces mejor rendimiento y alargando la vida útil de discos flash o sólidos. La única desventaja es usar algo de tiempo del procesador para realizar la compresión.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/pineapple-supply-co-SRKxB1B_sn4-unsplash.jpg"&gt;&lt;img src="https://sergiobelkin.com/images/pineapple-supply-co-SRKxB1B_sn4-unsplash.thumbnail.jpg" alt="zswap"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@pineapple?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Pineapple Supply Co.&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/tray-desktop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Mediante el caché se logra una diferenciación entre páginas más usadas (zswap) y menos usadas (swap).&lt;/p&gt;
&lt;h4 id="systemd-oomd"&gt;systemd-oomd&lt;/h4&gt;
&lt;p&gt;El servicio systemd-oomd es un proyecto en el que comenzó en Facebook para integrarlo con systemd. En un principio estaba pensado para manejo de memoria a gran escala, y bastante más complejo de configurar. Sin embargo ha sido adoptado desde Fedora 34 reemplazando a EarlyOOM. En las distribuciones que usan versiones recientes de systemd, está disponible, aunque no todas lo habilitan de manera predeterminada.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/systemd-oomd.webp"&gt;&lt;img src="https://sergiobelkin.com/images/systemd-oomd.thumbnail.webp" alt="systemd-oomd"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h3 id="resumen"&gt;Resumen&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Swap no es la villana de la película&lt;/li&gt;
&lt;li&gt;El tuning de cgroupv2 puede traer grandes beneficios, no obstante existen proyectos y distribuciones que no lo usan.&lt;/li&gt;
&lt;li&gt;EarlyOOM es una solución rápida y aplicable a una amplia gama de sistemas Linux, aunque no siempre es la más exacta ni más elegante.&lt;/li&gt;
&lt;li&gt;El servicio nohang (o nohang-desktop) es una opción más madura aunque algo más compleja que EarlyOOM, pero que sin embargo ha caído en cierta inactividad.&lt;/li&gt;
&lt;li&gt;El servicio &lt;strong&gt;systemd-oomd&lt;/strong&gt; inicialmente incorporado por Facebook es seguramente la opción más adecuada para escenarios más complejos y de manejo de memoria a gran escala. También es utilizado actualmente en sistemas de escritorio.&lt;/li&gt;
&lt;li&gt;Sin embargo, muchas distribuciones o &lt;em&gt;sabores&lt;/em&gt; de distribuciones prefieren usar el mecanismo clásico de OOM killer.&lt;/li&gt;
&lt;li&gt;A veces se sugiere el ajuste de parámetros del kernel mediante &lt;code&gt;sysctl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Si se desea ahorrar espacio en disco se puede reemplazar la swap por zram, sacrificando la opción de hibernar el sistema.&lt;/li&gt;
&lt;li&gt;La opción zswap es más sofisticada, aunque dependemos del uso de swap en disco.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@rollelflex_graphy726?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;sk&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/chess?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="fuentes-consultadas"&gt;Fuentes consultadas&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.suse.com/support/kb/doc/?id=000017834"&gt;Is a swap partition required for SLES? | Support | SUSE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Virtual_memory#Thrashing"&gt;Virtual memory - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Page_(computer_memory)"&gt;Page (computer memory) - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linux-mm.org/VirtualMemory?action=fullsearch&amp;amp;context=180&amp;amp;value=glossary"&gt;VirtualMemory - linux-mm.org Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kernelnewbies.org/KernelGlossary"&gt;KernelGlossary - Linux Kernel Newbies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lwn.net/Articles/411845/"&gt;Ghosts of Unix Past: a historical search for design patterns [LWN.net]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.vishalchovatiya.com/how-does-virtual-memory-work/#How_Does_Virtual_Memory_Work"&gt;How does virtual memory work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Paging#Page_stealing"&gt;Paging - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linux-mm.org/Memory_pressure"&gt;Memory_pressure - linux-mm.org Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kernel.org/doc/html/latest/admin-guide/mm/concepts.html?highlight=write%20back%20cache#virtual-memory-primer"&gt;Concepts overview — The Linux Kernel documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linux.com/news/all-about-linux-swap-space/"&gt;All about Linux swap space - Linux.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unix.stackexchange.com/questions/417855/why-does-linux-need-swap-space-in-a-vm"&gt;Why does Linux need swap space in a VM? - Unix &amp;amp; Linux Stack Exchange&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrisdown.name/2018/01/02/in-defence-of-swap.html"&gt;In defence of swap: common misconceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/nttlabs/cgroup-v2-596d035be4d7"&gt;The current adoption status of cgroup v2 in containers | by Akihiro Suda | nttlabs | Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://facebookmicrosites.github.io/cgroup2/docs/memory-controller.html"&gt;Memory Controller · cgroup2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git/tree/Documentation/admin-guide/cgroup-v2.rst"&gt;cgroup-v2.rst « admin-guide « Documentation - kernel/git/tj/cgroup.git - cgroup export tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/index.php/Cgroups#Switching_to_cgroups_v2"&gt;cgroups - ArchWiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kernel.org/doc/html/latest/admin-guide/blockdev/zram.html"&gt;zram: Compressed RAM-based block devices — The Linux Kernel documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fedoraproject.org/wiki/Changes/SwapOnZRAM#Summary"&gt;Changes/SwapOnZRAM - Fedora Project Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fedoraproject.org/wiki/Changes/EnableEarlyoom#Summary"&gt;Changes/EnableEarlyoom - Fedora Project Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man5/proc.5.html"&gt;proc(5) - Linux manual page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hakavlad/nohang"&gt;hakavlad/nohang: A sophisticated low memory handler for Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Zswap"&gt;zswap - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.phoronix.com/scan.php?page=news_item&amp;amp;px=Systemd-OOMD-April-WIP"&gt;Systemd-OOMD Continues Coming Together For Better Linux Out-Of-Memory Handling - Phoronix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=beefUhRH5lU"&gt;SREcon19 Asia/Pacific - Linux Memory Management at Scale: Under the Hood - YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.man7.org/linux/man-pages/man2/mmap.2.html"&gt;mmap(2) — Linux manual page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kernel.org/doc/html/v6.13/core-api/mm-api.html"&gt;Memory Management APIs¶&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fedoraproject.org/wiki/Changes/EnableSystemdOomd"&gt;Changes/EnableSystemdOomd&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><category>cgroups</category><category>kernel</category><category>RAM</category><category>sysadmin</category><guid>https://sergiobelkin.com/posts/plan-tactico-y-estrategico-de-la-memoria-en-linux/</guid><pubDate>Mon, 01 Dec 2025 05:00:32 GMT</pubDate></item><item><title>3 Power Tips + 1 Power Link I8</title><link>https://sergiobelkin.com/posts/3-power-tips-power-link-i8/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/PowerTipsPlus.png"&gt;&lt;/figure&gt; &lt;p&gt;&lt;strong&gt;Resumen&lt;/strong&gt;: En esta entrega, un ejemplo del poder que tienen la subshells de bash, como solucionar problemas de SELinux con Podman, y el uso de la herramienta yq para procesar archivos yaml.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Nota: En los ejemplos de comandos el prompt del usuario no privilegiado es "$", mientras que el del superusuario es "#"&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="power-tip-1-crear-entornos-de-bash-efimeros-usando-subshells"&gt;Power Tip #1: Crear entornos de bash efímeros usando subshells&lt;/h3&gt;
&lt;p&gt;Una subshell es una copia del proceso de la shell actual. Una manera de crear una subshell es usando &lt;code&gt;()&lt;/code&gt; y sirve: para personalizar el entorno de bash de manera reversible. Por ejemplo, podemos cambiar de directorio y la variable de entorno C (POSIX) para que muestre los mensajes de salida y de error en inglés:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LANG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
es_AR.UTF-8
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
/home/sergio
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ahora creamos una &lt;strong&gt;subshell&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"La variable LANG cambia a  &lt;/span&gt;&lt;span class="nv"&gt;$LANG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/algun_dir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"el directorio actual es &lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;myscript.sh&lt;span class="o"&gt;)&lt;/span&gt;
La&lt;span class="w"&gt; &lt;/span&gt;variable&lt;span class="w"&gt; &lt;/span&gt;LANG&lt;span class="w"&gt; &lt;/span&gt;cambia&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt;  &lt;/span&gt;C
-bash:&lt;span class="w"&gt; &lt;/span&gt;cd:&lt;span class="w"&gt; &lt;/span&gt;/algun_dir:&lt;span class="w"&gt; &lt;/span&gt;No&lt;span class="w"&gt; &lt;/span&gt;such&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;directory
el&lt;span class="w"&gt; &lt;/span&gt;directorio&lt;span class="w"&gt; &lt;/span&gt;actual&lt;span class="w"&gt; &lt;/span&gt;es&lt;span class="w"&gt; &lt;/span&gt;/tmp
-bash:&lt;span class="w"&gt; &lt;/span&gt;myscript.sh:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;found
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Al terminar la subshell, volvemos a la shell madre y tanto el directorio de trabajo como la variable LANG, vuelven a sus valores predeterminados:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
/home/sergio
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LANG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
es_AR.UTF-8
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="power-tip-2-identificar-y-solucionar-problemas-de-selinux-al-usar-volumenes"&gt;Power Tip #2: Identificar y solucionar problemas de SELinux al usar volúmenes.&lt;/h3&gt;
&lt;p&gt;Supongamos que necesitamos probar una configuración en un contenedor:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;podman&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt;  &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;mynginx&lt;span class="w"&gt;  &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/sandbox/nginx.conf:/etc/nginx/nginx.conf&lt;span class="w"&gt; &lt;/span&gt;--pull&lt;span class="o"&gt;=&lt;/span&gt;never&lt;span class="w"&gt; &lt;/span&gt;docker.io/library/nginx@sha256:553f64aecdc31b5bf944521731cd70e35da4faed96b2b7548a3d8e2598c52a42
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ahora bien: ¿Qué sucede si el contenedor en realidad, falla al arrancar como lo muestran los logs?:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;podman&lt;span class="w"&gt; &lt;/span&gt;logs&lt;span class="w"&gt; &lt;/span&gt;mynginx&lt;span class="w"&gt; &lt;/span&gt;
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;empty,&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;attempt&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;perform&lt;span class="w"&gt; &lt;/span&gt;configuration
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Looking&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;scripts&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Launching&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
&lt;span class="m"&gt;10&lt;/span&gt;-listen-on-ipv6-by-default.sh:&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;Getting&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;checksum&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;/etc/nginx/conf.d/default.conf
&lt;span class="m"&gt;10&lt;/span&gt;-listen-on-ipv6-by-default.sh:&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;Enabled&lt;span class="w"&gt; &lt;/span&gt;listen&lt;span class="w"&gt; &lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;IPv6&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/nginx/conf.d/default.conf
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Sourcing&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Launching&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Launching&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Configuration&lt;span class="w"&gt; &lt;/span&gt;complete&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ready&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;up
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;:09:21&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;emerg&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: open() "/etc/nginx/nginx.conf" failed (13: Permission denied)&lt;/span&gt;
nginx:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;emerg&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;open&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/etc/nginx/nginx.conf"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;failed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;Permission&lt;span class="w"&gt; &lt;/span&gt;denied&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Si estás tentado a desactivar SELinux, te recomiendo enfáticamente que leas &lt;a href="https://sergiobelkin.com/posts/selinux-nah-deshabilitalo/"&gt;¿SELinux? Nah, dejalo en Disabled&lt;/a&gt;. Lo que ocurre es que no hay ninguna regla que permita que el contenedor acceda al contexto del archivo nginx.conf del host.&lt;/p&gt;
&lt;p&gt;Contexto del archivo:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-Z&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/sandbox/nginx.conf
unconfined_u:object_r:user_home_t:s0&lt;span class="w"&gt; &lt;/span&gt;/home/sergio/sandbox/nginx.conf
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Este problema se soluciona fácilmente:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;podman&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;mynginx
podman&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;mynginx
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Y ahora el contenedor arrancará sin inconvenientes:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;podman&lt;span class="w"&gt; &lt;/span&gt;ps
CONTAINER&lt;span class="w"&gt; &lt;/span&gt;ID&lt;span class="w"&gt;  &lt;/span&gt;IMAGE&lt;span class="w"&gt;                                                                                            &lt;/span&gt;COMMAND&lt;span class="w"&gt;               &lt;/span&gt;CREATED&lt;span class="w"&gt;         &lt;/span&gt;STATUS&lt;span class="w"&gt;         &lt;/span&gt;PORTS&lt;span class="w"&gt;       &lt;/span&gt;NAMES
a29c08eda1b1&lt;span class="w"&gt;  &lt;/span&gt;docker.io/library/nginx@sha256:553f64aecdc31b5bf944521731cd70e35da4faed96b2b7548a3d8e2598c52a42&lt;span class="w"&gt;  &lt;/span&gt;nginx&lt;span class="w"&gt; &lt;/span&gt;-g&lt;span class="w"&gt; &lt;/span&gt;daemon&lt;span class="w"&gt; &lt;/span&gt;o...&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;seconds&lt;span class="w"&gt; &lt;/span&gt;ago&lt;span class="w"&gt;  &lt;/span&gt;Up&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;seconds&lt;span class="w"&gt;              &lt;/span&gt;mynginx
podman&lt;span class="w"&gt; &lt;/span&gt;logs&lt;span class="w"&gt; &lt;/span&gt;mynginx&lt;span class="w"&gt; &lt;/span&gt;
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;empty,&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;attempt&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;perform&lt;span class="w"&gt; &lt;/span&gt;configuration
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Looking&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;scripts&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Launching&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
&lt;span class="m"&gt;10&lt;/span&gt;-listen-on-ipv6-by-default.sh:&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;Getting&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;checksum&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;/etc/nginx/conf.d/default.conf
&lt;span class="m"&gt;10&lt;/span&gt;-listen-on-ipv6-by-default.sh:&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;Enabled&lt;span class="w"&gt; &lt;/span&gt;listen&lt;span class="w"&gt; &lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;IPv6&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/nginx/conf.d/default.conf
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Sourcing&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Launching&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Launching&lt;span class="w"&gt; &lt;/span&gt;/docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh:&lt;span class="w"&gt; &lt;/span&gt;Configuration&lt;span class="w"&gt; &lt;/span&gt;complete&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ready&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;up
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: using the "epoll" event method&lt;/span&gt;
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: nginx/1.29.3&lt;/span&gt;
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: built by gcc 14.2.0 (Debian 14.2.0-19) &lt;/span&gt;
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: OS: Linux 4.18.0-553.84.1.el8_10.x86_64&lt;/span&gt;
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: getrlimit(RLIMIT_NOFILE): 262144:262144&lt;/span&gt;
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: start worker processes&lt;/span&gt;
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: start worker process 28&lt;/span&gt;
&lt;span class="m"&gt;2025&lt;/span&gt;/11/24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;:16:10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;notice&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;#1: start worker process 29&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ahora bien, la opción &lt;code&gt;:Z&lt;/code&gt; no es mágica, para tener una aproximación a lo que hace veamos el contexto del archivo $HOME/sandbox/nginx.conf:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-Z&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/sandbox/nginx.conf
system_u:object_r:container_file_t:s0:c369,c839&lt;span class="w"&gt; &lt;/span&gt;/home/sergio/sandbox/nginx.conf
&lt;/pre&gt;&lt;/div&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Antes&lt;/th&gt;
&lt;th&gt;Después&lt;/th&gt;
&lt;th&gt;Comentario&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Usuario (SELinux)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;unconfined_u&lt;/td&gt;
&lt;td&gt;system_u&lt;/td&gt;
&lt;td&gt;Pasa de ser un usuario sin restricciones a un usuario gestionado por el sistema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rol (SELinux)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;object_r&lt;/td&gt;
&lt;td&gt;object_r&lt;/td&gt;
&lt;td&gt;Se mantiene sin cambios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Asignación basada en tipos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;user_home_t&lt;/td&gt;
&lt;td&gt;container_file_t&lt;/td&gt;
&lt;td&gt;Cambia a un tipo de asignación en la cual los contenedores pueden escribir&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rango (SELinux)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;s0&lt;/td&gt;
&lt;td&gt;s0:c369,c839&lt;/td&gt;
&lt;td&gt;Este cambio implica que solamente podrá utilizarlo de manera exclusiva un único contenedor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="power-tip-3-filtrar-un-archivo-de-template-en-formato-yaml-de-zabbix-sin-instalar-absolutamente-nada"&gt;Power Tip #3: Filtrar un archivo de template en formato yaml de Zabbix sin instalar absolutamente nada&lt;/h3&gt;
&lt;p&gt;Solamente necesitamos Podman, en el siguiente ejemplo estamos filtrando todos los triggers estáticos con nivel de severidad &lt;strong&gt;HIGH&lt;/strong&gt; o &lt;strong&gt;DISASTER&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;podman&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/workdir:Z&lt;span class="w"&gt; &lt;/span&gt;mikefarah/yq&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'.zabbix_export.templates[].items[].triggers[] | select(.priority == "HIGH" or .priority == "DISASTER")| {"Trigger": .name}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;template_db_mssql_agent2.yaml
Trigger:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'MSSQL: Percentage of the buffer cache efficiency is low'&lt;/span&gt;
Trigger:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'MSSQL: Page life expectancy is low'&lt;/span&gt;
Trigger:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'MSSQL: Percentage of work tables available from the work table cache is low'&lt;/span&gt;
Trigger:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'MSSQL: Service is unavailable'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="fuentes-y-mas-recursos"&gt;Fuentes y más recursos&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://danwalsh.livejournal.com/81269.html"&gt;Container Labeling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://danwalsh.livejournal.com/76016.html"&gt;Be careful relabeling volumes with Container run times. Sometimes things can go very wrong?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="power-link"&gt;Power Link&lt;/h3&gt;
&lt;p&gt;El enlace de esta edición apunta a una entrevista a Linux Torvalds que gira en torno a la evolución del hardware, la IA y su impacto en el Linux y el desarrollo del kernel: &lt;a href="https://www.youtube.com/watch?v=NjGHrDnPxwI"&gt;Linus Torvalds — Talks about AI Hype, GPU Power, and Linux’s Future &lt;/a&gt;. En la actualidad cuestiones como la programación guiada por la intuición con la ayuda de chatbots debates importantes, no solamente en Linux sino en el ámbito IT en general.&lt;/p&gt;</description><category>bash</category><category>podman</category><category>seguridad</category><category>SELinux</category><category>yq</category><category>zabbix</category><guid>https://sergiobelkin.com/posts/3-power-tips-power-link-i8/</guid><pubDate>Sun, 23 Nov 2025 22:09:59 GMT</pubDate></item><item><title>3 Power Tips + 1 Power Link I7</title><link>https://sergiobelkin.com/posts/3-power-tips-power-link-i7/</link><dc:creator>sebelk</dc:creator><description>&lt;figure&gt;&lt;img src="https://sergiobelkin.com/images/PowerTipsPlus.png"&gt;&lt;/figure&gt; &lt;p&gt;&lt;strong&gt;Resumen&lt;/strong&gt;: Tips para bash, KDE Plasma y el uso del shell para cambiar entre ambientes de desarrollo. Además, en el Power Link, un artículo de Aaron P. MacSween, en el que discute el uso de &lt;em&gt;AI scraping&lt;/em&gt; de sitios web usados de manera no consensuada para entrenar &lt;em&gt;Modelos Extensos de Lenguaje (LLMs)&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="power-tip-1-agrupacion-de-comandos-en-bash"&gt;Power Tip #1: Agrupación de comandos en bash&lt;/h3&gt;
&lt;p&gt;El shell bash permite agrupar comandos, una funcionalidad tan sencilla como potente, por ejemplo:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dnf&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;upgrade&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;httpd&lt;span class="w"&gt; &lt;/span&gt;mariadb&lt;span class="w"&gt; &lt;/span&gt;php-fpm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The final status is unsuccessful 🙁"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;De esta manera si algunos de los dos comandos falla, mostrará un mensaje de error unificado.&lt;/p&gt;
&lt;h3 id="power-tip-2-uso-de-cuadros-de-dialogos-nativos-de-kde-plasma-en-firefox"&gt;Power Tip #2: Uso de cuadros de diálogos nativos de KDE Plasma en Firefox&lt;/h3&gt;
&lt;p&gt;Para hacerlo hay que abrir la url &lt;code&gt;about:config&lt;/code&gt; y luego setear la preferencia &lt;code&gt;widget.use-xdg-desktop-portal.file-picker&lt;/code&gt; en &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/pt7-set-pref-ff.webp"&gt;&lt;img src="https://sergiobelkin.com/images/pt7-set-pref-ff.thumbnail.webp" alt="Set preferences on Firefox"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Esto se basa en el uso de XDG Portals que es un elemento de la arquitectura de entornos gráficos. Ellos proporcionan sandboxing, funcionalidad en entornos Wayland y lo que nos interesa en este caso: coherencia en la interfaz del usuario.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Antes 🤐&lt;/th&gt;
&lt;th&gt;Después 😎️&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/pt7-ugly-dialog-box-firefox.webp"&gt;&lt;img src="https://sergiobelkin.com/images/pt7-ugly-dialog-box-firefox.thumbnail.webp" alt="Set preferences on Firefox"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a class="image-reference" href="https://sergiobelkin.com/images/pt7-nice-dialog-box-firefox.webp"&gt;&lt;img src="https://sergiobelkin.com/images/pt7-nice-dialog-box-firefox.thumbnail.webp" alt="Before and After of setting file picker"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ah... usar la variable de entorno &lt;code&gt;MOZ_USE_XDG_PORTAL&lt;/code&gt; no es la manera recomendada, más allá de lo que diga cierto chatbot...&lt;/p&gt;
&lt;h3 id="power-tip-3-flask-linux-entornos-seguros-usando-solo-la-shell"&gt;Power Tip #3: [Flask + Linux]: Entornos Seguros usando solo la Shell&lt;/h3&gt;
&lt;h4 id="problema-comun"&gt;Problema Común&lt;/h4&gt;
&lt;p&gt;Ejecutar una app Flask con &lt;code&gt;DEBUG=True&lt;/code&gt; en producción expone trazas de errores, rutas internas y detalles del &lt;em&gt;stack&lt;/em&gt; que pueden ser explotados. Un &lt;strong&gt;sysadmin o devops con experiencia&lt;/strong&gt; sabe que una práctica profesional es &lt;strong&gt;externalizar la configuración&lt;/strong&gt; y usar el sistema operativo como interruptor de entorno.&lt;/p&gt;
&lt;h4 id="solucion-basada-en-linux-flask"&gt;Solución Basada en Linux + Flask&lt;/h4&gt;
&lt;p&gt;Usá la variable de entorno de Linux &lt;code&gt;FLASK_ENV&lt;/code&gt; para alternar entre una configuración segura de Producción (&lt;code&gt;config_prod.py&lt;/code&gt;) y una configuración orientada a Desarrollo (&lt;code&gt;config_dev.py&lt;/code&gt;).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Archivo&lt;/th&gt;
&lt;th&gt;Ajuste&lt;/th&gt;
&lt;th&gt;Enfoque&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;config_dev.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DEBUG = True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Depuración y agilidad.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;config_prod.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DEBUG = False&lt;/code&gt; (más &lt;code&gt;SECRET_KEY&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Seguridad&lt;/strong&gt; y &lt;strong&gt;Gestión de Secretos&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;En &lt;code&gt;config_prod.py&lt;/code&gt;, además de &lt;code&gt;DEBUG = False&lt;/code&gt;, la clave secreta se carga
desde el entorno con &lt;code&gt;SECRET_KEY = os.environ.get('PROD_KEY')&lt;/code&gt; — así nunca
queda hardcodeada en el repo.&lt;/p&gt;
&lt;h4 id="fragmento-de-apppy"&gt;Fragmento de app.py&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;flask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="c1"&gt;# Se importan las clases para un código robusto:&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;config_dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DevelopmentConfig&lt;/span&gt; 
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;config_prod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProductionConfig&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'FLASK_ENV'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Entorno PROD: Carga DEBUG=False&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProductionConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Entorno DEV: Carga DEBUG=True por defecto&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DevelopmentConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id="control-desde-la-terminal"&gt;Control desde la Terminal&lt;/h4&gt;
&lt;p&gt;+--------------------------+----------------------------------------------------------------------------------+---------------------------------------------------+
| Entorno                  | Comando                                                                          | Resultado                                         |
+==========================+==================================================================================+===================================================+
| &lt;strong&gt;Producción&lt;/strong&gt;           | &lt;code&gt;export FLASK_ENV=production&lt;/code&gt;                                                    | &lt;code&gt;Debug mode: off&lt;/code&gt;                                 |
|                          |                                                                                  |                                                   |
|                          | &lt;code&gt;python app.py&lt;/code&gt;                                                                  | (modo seguro)                                     |
+--------------------------+----------------------------------------------------------------------------------+---------------------------------------------------+
| &lt;strong&gt;Desarrollo&lt;/strong&gt;           | &lt;code&gt;unset FLASK_ENV&lt;/code&gt;                                                                | &lt;code&gt;Debug mode: on&lt;/code&gt;                                  |
|                          |                                                                                  |                                                   |
|                          | &lt;code&gt;python app.py&lt;/code&gt;                                                                  | (vuelve a depuración)                             |
+--------------------------+----------------------------------------------------------------------------------+---------------------------------------------------+&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;✅ Conclusión:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Este patrón convierte tu &lt;em&gt;shell&lt;/em&gt; de Linux en un &lt;em&gt;switch&lt;/em&gt; seguro de entorno para tu app Flask.&lt;/p&gt;
&lt;h3 id="preguntas-de-repaso"&gt;Preguntas de repaso&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Seguridad y DevOps (Power Tip #3):&lt;/strong&gt; ¿Cuál es el riesgo principal de ejecutar una aplicación Flask con &lt;code&gt;DEBUG=True&lt;/code&gt; en producción, y cómo permite la variable de entorno de Linux &lt;strong&gt;&lt;code&gt;FLASK_ENV&lt;/code&gt;&lt;/strong&gt; que el administrador (&lt;code&gt;sysadmin o devops con experiencia&lt;/code&gt;) cambie de manera segura a la configuración de producción (&lt;code&gt;config_prod.py&lt;/code&gt;) usando solo un comando en la &lt;em&gt;shell&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eficiencia en Bash (Power Tip #1):&lt;/strong&gt; En el ejemplo de agrupación de comandos, ¿cuál es el propósito de encerrar las sentencias de actualización (&lt;code&gt;dnf -y upgrade&lt;/code&gt;) y de reinicio de servicios (&lt;code&gt;systemctl restart ...&lt;/code&gt;) entre llaves &lt;code&gt;{}&lt;/code&gt;, y qué rol juega el operador lógico &lt;strong&gt;&lt;code&gt;||&lt;/code&gt;&lt;/strong&gt; en caso de que alguna de estas operaciones falle?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Experiencia de Usuario (Power Tip #2):&lt;/strong&gt; Si un usuario de KDE Plasma desea que Firefox utilice los cuadros de diálogo nativos del escritorio en lugar de los predeterminados del navegador, ¿qué URL debe abrir y a qué valor debe establecer la preferencia &lt;strong&gt;&lt;code&gt;widget.use-xdg-desktop-portal.file-picker&lt;/code&gt;&lt;/strong&gt;?&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="power-link"&gt;Power Link&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://cryptography.dog/blog/AI-scrapers-request-commented-scripts/"&gt;AI scrapers request commented scripts&lt;/a&gt;. ¿Hasta qué punto una actividad es considerada benigina cuando se entrenan LLMs? Y muchas cuestiones para pensar...&lt;/p&gt;</description><category>bash</category><category>firefox</category><category>flask</category><category>kde-plasma</category><category>python</category><category>seguridad</category><guid>https://sergiobelkin.com/posts/3-power-tips-power-link-i7/</guid><pubDate>Tue, 04 Nov 2025 22:10:17 GMT</pubDate></item></channel></rss>