<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/rss/rss-styles.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Crstian&#039;s Blog</title>
    <description>Cristian&#039;s blog, SRE / AI Platform Engineer: Kubernetes, observability, inference platforms, homelab and open source.</description>
    <link>https://blog.crstian.me/en/</link>
    <language>en-US</language>
    <managingEditor>Crstian</managingEditor>
    <webMaster>Crstian</webMaster>
    <author>Crstian</author>
    <pubDate>2026-10-13T12:32:58.153Z</pubDate>
    <lastBuildDate>2026-10-13T12:32:58.153Z</lastBuildDate>
    <generator>Astro Litos Theme</generator>
    <atom:link href="https://blog.crstian.me/en/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Iris-3B against the big ones: a 3B image model on a single GPU</title>
      <link>https://blog.crstian.me/en/post/2026-10-10-iris-3b-vs-budgetpixel-arena/</link>
      <guid>https://blog.crstian.me/en/post/2026-10-10-iris-3b-vs-budgetpixel-arena/</guid>
      <updated>2026-10-10T00:00:00.000Z</updated>
      <pubDate>2026-10-10T00:00:00.000Z</pubDate>
      <description><![CDATA[I tried Iris-3B, an open 3B-parameter image model that generates in pixel space with no VAE, on the same BudgetPixel Arena prompts where GPT Image, FLUX and friends compete.]]></description>
      <content:encoded><![CDATA[<img src="https://blog.crstian.me/_astro/iris-04-sauna.B24arLtj_ZnJqdC.webp" alt="Iris-3B against the big ones: a 3B image model on a single GPU" style="width: 100%; height: auto; margin-bottom: 1em;" />
<p>This week a new open image model came out of Spain 🇪🇸: <strong>Iris-3B</strong>, with just 3B parameters. I set it up on a GPU and put it against GPT Image, FLUX and friends with the same prompts.</p>
<p>One image per prompt, no fuss. Here are the results, since the prompts and the details don’t fit in the <a href="https://x.com/crstiax" rel="noopener noreferrer" target="_blank">Twitter thread</a> (in Spanish).</p>
<p>Before anyone jumps at me: I know this isn’t a fair comparison. I’m not claiming a 3B model beats GPT Image. What I wanted to see is how far such a small model is from the best ones and what it can be useful for.</p>
<h2>What Iris-3B is</h2>
<p>It comes from <a href="https://speridlabs.com/research/iris" rel="noopener noreferrer" target="_blank">speridlabs</a> and it’s Apache 2.0. It has 3B parameters, plus a frozen 4B text encoder (Qwen3-VL-4B-Instruct).</p>
<p>On their site they compare it with much bigger models, and it ties with them on one of the benchmarks:</p>

































<table><thead><tr><th>Benchmark (1024²)</th><th>Iris-3B</th><th>FLUX.1-dev 12B</th><th>Z-Image 6B</th><th>Qwen-Image 20B</th></tr></thead><tbody><tr><td>OneIG</td><td><strong>0.540</strong></td><td>0.434</td><td>0.546</td><td>0.539</td></tr><tr><td>DPG</td><td>86.5</td><td>83.8</td><td>88.1</td><td>88.3</td></tr><tr><td>GenEval</td><td>0.80</td><td>0.66</td><td>0.84</td><td>0.87</td></tr></tbody></table>
<p>Keep in mind these numbers are their own. On DPG and GenEval it trails Z-Image and Qwen-Image, but beats FLUX.1-dev, which has 12B.</p>
<p>They also say where it struggles: long text inside the image, positions and counting objects.</p>
<h2>What “no VAE” means</h2>
<p>What caught my eye the most is that it generates the image directly in pixels, with no VAE.</p>
<p>Almost every image model (Stable Diffusion, FLUX…) doesn’t generate the image as such. A VAE (<em>Variational Autoencoder</em>) compresses it into something much smaller, the latent space. The model works on that, and at the end the same VAE turns it back into pixels. In FLUX, for example, a 1024×1024 image is handled as a 128×128 grid.</p>
<p>Like everything, this has its upsides and downsides. Compressing saves a huge amount of compute, but you lose information, and it shows mostly in textures and fine detail.</p>
<p>Iris skips that compression step and generates the pixels directly. It’s more expensive to compute, but nothing in between eats away detail.</p>
<h2>How I tested it</h2>
<p>I took the prompts from <a href="https://budgetpixel.com/arena/prompts" rel="noopener noreferrer" target="_blank">BudgetPixel Arena</a>. There, around 20 models render the same prompt and people vote without knowing which is which. Once voting closes, you can see which model made each image and by how much it won.</p>
<p>With Iris I did this:</p>
<ul>
<li>The prompt text as is, untouched.</li>
<li>1024×1024, seed 42, 100 steps and CFG 3.</li>
<li>A single run per prompt. Whatever came out, goes.</li>
</ul>
<p>In each comparison the Iris image comes first, with the arena winner next to it. The arena images are by BudgetPixel AI, licensed CC BY 4.0.</p>
<h2>Architecture: the sauna</h2>
<blockquote><p>Exterior of a Scandinavian modernist lakeside sauna pavilion, blackened timber cladding, pale granite plinth, copper roof, winter blue-hour light glowing through narrow windows, viewed from a snowy reed-lined path, straight structural geometry and plausible construction.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-04-sauna.B24arLtj_ZnJqdC.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-04-sauna-gptimage2.ZoX7Lq7l_2vcA9K.webp" alt="GPT Image 2" /></div><p>One of my favourites. Charred timber, granite, copper roof, everything in place and with clean geometry.</p>
<p>That said, the prompt asked for warm light coming through the windows and there’s no trace of it, and it looks a bit like a render. In the arena, GPT Image 2 won with 80% of the votes (<a href="https://budgetpixel.com/arena/prompts/exterior-scandinavian-modernist-lakeside-sauna-451a" rel="noopener noreferrer" target="_blank">see all 20 versions</a>).</p>
<h2>Landscape: the swamp</h2>
<blockquote><p>A cypress swamp at foggy sunrise after overnight drizzle, dark tea-colored water threading between moss-draped trunks; slick root knees and floating leaves lead toward pale trees disappearing into layered mist.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-03-pantano.DGL-DXt6_Z2dk1G8.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-03-pantano-muse1.BX3bI2P1_1Jnng4.webp" alt="Muse Image 1.0" /></div><p>The fog, the roots and the floating leaves look great. But the water was supposed to be tea-coloured and it came out almost red.</p>
<p>In the arena, Muse Image 1.0 and Qwen Image 3.0 won with 68% (<a href="https://budgetpixel.com/arena/prompts/cypress-swamp-foggy-sunrise-after-overnight-1e34" rel="noopener noreferrer" target="_blank">see all 20 versions</a>).</p>
<h2>Photography: portrait and documentary</h2>
<blockquote><p>Editorial portrait of an adult man in his early 40s wearing a charcoal wool fisherman’s sweater, one hand loosely holding its ribbed cuff. His thoughtful, slightly tired expression rests against a muted ochre studio backdrop, shaped by gentle directional window light.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-01-retrato.ByvpjJMU_Z47pu1.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-01-retrato-flux3.BWjN1WoF_HS3v3.webp" alt="FLUX 3 Image" /></div><blockquote><p>Candid documentary photograph of an adult bicycle mechanic repairing a flat tire outside a neighborhood workshop, soft overcast daylight, 35mm lens with moderate depth of field, greasy fingerprints, dust, and tiny metal shavings visible on the workbench.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-02-mecanico-bici.DgzWqIuR_Z23o9lx.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-02-mecanico-bici-klingv3.BRS9aEfa_Z1BkBGp.webp" alt="Kling v3" /></div><p>Photography is where it surprised me the most. Both pass for real photos, and the mechanic one has the grease and the metal shavings on the bench.</p>
<p>Where it slips is the details. The sweater was supposed to be a cable-knit fisherman’s sweater and it came out as a plain crew neck, plus a few small artefacts in the top left. FLUX 3 Image won with 91% (<a href="https://budgetpixel.com/arena/prompts/editorial-portrait-adult-man-his-early-d578" rel="noopener noreferrer" target="_blank">portrait</a>) and Kling v3 with 88% (<a href="https://budgetpixel.com/arena/prompts/candid-documentary-photograph-adult-bicycle-f444" rel="noopener noreferrer" target="_blank">mechanic</a>).</p>
<h2>Illustration: watercolour and mechanical heart</h2>
<blockquote><p>Ink-and-watercolour illustration of an adult astronomer mapping constellations from a glass-domed observatory, surrounded by brass instruments and flowering night plants. Indigo, lavender, and pale gold palette, delicate linework, serene atmospheric detail.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-05-acuarela-astronomo.DamdTUQr_ZMxnH9.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-05-acuarela-astronomo-gptimage25-sunburst.CKQfatOU_M06GQ.webp" alt="GPT Image 2.5 Sunburst" /></div><blockquote><p>A beautifully intricate mechanical heart floating alone in deep darkness, designed like an exquisite piece of horological sculpture. Its polished brass, copper, and dark steel chambers are partially open, revealing layers of tiny interlocking gears, delicate springs, pistons, and precision mechanisms within. Sections of crystal-clear glass expose deeper workings, while slender tubes carry a faintly glowing fluid through the heart like an artificial bloodstream. Tiny gears turn and pistons move, suggesting that the heart is genuinely functioning. Fine mechanical filaments curl outward like veins and arteries, catching subtle highlights from the warm inner glow. Elegant steampunk aesthetic, dramatic cinematic lighting, rich metallic textures, intricate reflections, mysterious atmosphere.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-06-corazon-mecanico.CKebM_aj_ZEy8dz.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-06-corazon-mecanico-gptimage25-sunburst.Cemosql4_23xWqq.webp" alt="GPT Image 2.5 Sunburst" /></div><p>The watercolour nails the style, the palette, the brass and the flowers. The heart is very good, glowing fluid tube and all, though less intricate than the prompt asks for.</p>
<p>GPT Image 2.5 Sunburst won both, the heart with 100% of the votes, so the bar was high (<a href="https://budgetpixel.com/arena/prompts/ink-watercolour-illustration-adult-astronomer-0f76" rel="noopener noreferrer" target="_blank">watercolour</a>, <a href="https://budgetpixel.com/arena/prompts/beautifully-intricate-mechanical-heart-floating-fb69" rel="noopener noreferrer" target="_blank">heart</a>).</p>
<h2>Its weak spots: counting and text</h2>
<p>Since they say it struggles with counting objects and with long text, I went straight for that.</p>
<blockquote><p>Exactly three green apples and two yellow pears on a white plate, the plate on a blue tablecloth, nothing else on the table.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-07-contar-fruta.DZ4jcwJi_ZcNXEq.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-07-contar-fruta-imagineart15pro.DH3KYkeb_28qgWK.webp" alt="ImagineArt 1.5 Pro" /></div><p>Three apples and two pears, first try. In the arena, ImagineArt 1.5 Pro won with 100% (<a href="https://budgetpixel.com/arena/prompts/adh-count" rel="noopener noreferrer" target="_blank">see all 20 versions</a>).</p>
<blockquote><p>Inside a quiet clockmaker’s workshop, brass gears and tiny tools gleam beneath green-shaded lamps; “ORRERY HOUSE” is engraved in crisp serif letters on a dark walnut sign above the repair counter.</p></blockquote>
<div><img src="https://blog.crstian.me/_astro/iris-08-texto-relojero.BTa0LsCI_yN27P.webp" alt="Iris-3B" /><img src="https://blog.crstian.me/_astro/ganador-08-texto-relojero-gptimage25-flare.BltaYhTT_Z1Hxod1.webp" alt="GPT Image 2.5 Flare" /></div><p>It got the text right too. Not a single wrong letter, green lamps included, although the sign looks painted rather than engraved.</p>
<p>Mind you, it’s two words. They say it struggles with long sentences and I haven’t tried that. GPT Image 2.5 Flare won with 88% (<a href="https://budgetpixel.com/arena/prompts/inside-quiet-clockmaker-workshop-brass-gears-682d" rel="noopener noreferrer" target="_blank">see all 20 versions</a>).</p>
<p>And before anyone gets carried away: in an earlier run, at 1344×768 and with another seed, the fruit came out as 2 and 2. With one image per prompt there’s a lot of luck involved, so if you try it, generate several.</p>
<h2>How long it takes</h2>
<p>I ran it on an NVIDIA RTX PRO 6000 Blackwell Max-Q (96 GB) I had set up for a talk. PyTorch 2.11 with CUDA 13.0, the official code and its Gradio demo as they come: FP32 weights, BF16 generation, no quantisation and no tuning.</p>



































<table><thead><tr><th>Test (1024×1024)</th><th>Time per image</th><th>Peak VRAM</th></tr></thead><tbody><tr><td>100 steps (default)</td><td><strong>32.5 s</strong></td><td>25.9 GiB</td></tr><tr><td>50 steps</td><td>16.6 s</td><td>25.9 GiB</td></tr><tr><td>25 steps</td><td>8.4 s</td><td>25.9 GiB</td></tr><tr><td>Batch of 8, 100 steps</td><td>38.1 s</td><td>35.5 GiB</td></tr><tr><td>512×512, 100 steps</td><td>10.0 s</td><td>25.0 GiB</td></tr></tbody></table>
<p>Batching doesn’t help. A single image already keeps the GPU at 100% (about 325 W), so it’s a compute-bound workload, the opposite of generating text with an LLM.</p>
<h2>Wrapping up</h2>
<p>For an open 3B model, I’m pretty impressed. It doesn’t beat the best ones, but it gets closer than I expected, and you can run it on a single GPU under Apache 2.0.</p>
<p>If you want to try it:</p>
<ul>
<li>Model: <a href="https://huggingface.co/speridlabs/iris-3b" rel="noopener noreferrer" target="_blank">huggingface.co/speridlabs/iris-3b</a></li>
<li>Paper: <a href="https://arxiv.org/abs/2610.09450" rel="noopener noreferrer" target="_blank">arXiv 2610.09450</a></li>
<li>Code: <a href="https://github.com/speridlabs/iris-3b" rel="noopener noreferrer" target="_blank">github.com/speridlabs/iris-3b</a></li>
<li>Demo: <a href="https://huggingface.co/spaces/speridlabs/iris-3b" rel="noopener noreferrer" target="_blank">huggingface.co/spaces/speridlabs/iris-3b</a></li>
<li>Arena prompts and images: <a href="https://budgetpixel.com/arena/prompts" rel="noopener noreferrer" target="_blank">BudgetPixel Arena</a> (BudgetPixel AI, CC BY 4.0)</li>
</ul>]]></content:encoded>
      <author>Crstian</author>
      <category>ai</category><category>text-to-image</category><category>iris-3b</category><category>diffusion</category><category>gpu</category>
    </item>
    <item>
      <title>Correlation of Metrics and Logs on Grafana and Loki</title>
      <link>https://blog.crstian.me/en/post/2024-05-02-correlacion-metricas/</link>
      <guid>https://blog.crstian.me/en/post/2024-05-02-correlacion-metricas/</guid>
      <updated>2024-05-02T00:00:00.000Z</updated>
      <pubDate>2024-05-02T00:00:00.000Z</pubDate>
      <description><![CDATA[Correlation of metrics and logs in Grafana, using tools such as Loki, is a process that allows users and system administrators to link monitoring information…]]></description>
      <content:encoded><![CDATA[
<p>Correlation of metrics and logs in Grafana, using tools such as Loki, is a process that allows users and system administrators to link monitoring information (metrics) with event logs (logs) to gain a more comprehensive understanding of the behavior and performance of their systems and applications.</p>
<h2>Basic Concepts:</h2>
<ul>
<li>
<p><strong>Metrics</strong>: These are quantitative measures that provide information about the performance of a system. Examples include CPU usage, memory, network latency, and more. In Grafana, these are usually visualized in dashboards using time series.</p>
</li>
<li>
<p>Logs: These are records of events that occur within an application or system. They contain detailed information about system performance, errors, transactions, etc. Loki is a popular tool to manage logs, allowing their storage, search and visualization.</p>
</li>
</ul>
<h2>Correlation in Grafana and Loki:</h2>
<ol>
<li>
<p><strong>Data Integration</strong>: Grafana allows you to integrate metrics and logs into a single dashboard. For example, you can have time series graphs showing resource usage along with dashboards showing relevant logs.</p>
</li>
<li>
<p><strong>Contextual Searching</strong>: When examining specific metrics, such as a spike in CPU usage, you can query Loki to find logs during that specific period. This helps to quickly identify the root cause of problems observed in the metrics.</p>
</li>
<li>
<p>**Log Enrichment: Loki allows the enrichment of logs with tags and metadata, which facilitates their correlation with specific metrics. For example, you can tag logs with information about the environment, the service or the application instance, facilitating its search and correlation with metrics.</p>
</li>
<li>
<p>**Alerts and Analytics: Grafana allows you to configure alerts based on metrics and logs. For example, a high CPU usage alert can be correlated with specific error logs, providing a more detailed view of potential problems.</p>
</li>
<li>
<p><strong>Temporal Visualization</strong>: Grafana’s ability to visualize temporal data facilitates visual correlation. You can see how events recorded in the logs align with changes in metrics over time.</p>
</li>
</ol>
<h2>Best Practices:</h2>
<ul>
<li><strong>Consistency in Labeling</strong>: Maintain consistent nomenclature for labels in both metrics and logs.</li>
<li>Synchronized Timing**: Ensure that timestamps on metrics and logs are synchronized to facilitate correlation.</li>
<li>Integrated Dashboards**: Design dashboards that combine metrics and logs in a way that reflects the relationship between the different types of data.</li>
</ul>
<p>In summary, the correlation of metrics and logs in Grafana, leveraging Loki tools, provides a comprehensive view of the status and performance of systems, allowing to identify and solve problems more efficiently and effectively.</p>]]></content:encoded>
      <author>Crstian</author>
      <category>Loki</category><category>grafana</category><category>logs</category><category>kubernetes</category><category>monitoring</category><category>k8s</category>
    </item>
    <item>
      <title>Loki as a log database</title>
      <link>https://blog.crstian.me/en/post/2024-05-02-grafana-loki/</link>
      <guid>https://blog.crstian.me/en/post/2024-05-02-grafana-loki/</guid>
      <updated>2024-05-02T00:00:00.000Z</updated>
      <pubDate>2024-05-02T00:00:00.000Z</pubDate>
      <description><![CDATA[In the market we have different database providers to save and later consult our logs, but in this case we are going to talk about Loki.]]></description>
      <content:encoded><![CDATA[
<p>In the market we have different database providers to save and later consult our logs, but in this case we are going to talk about Loki.</p>
<h2>Loki</h2>
<p>Loki is a log management tool that focuses on indexing only log metadata, such as Prometheus-style tags. The log data is stored compressed in blocks in storages such as S3, GC or locally. Loki’s index is based on these tags, leaving the original log message unindexed. However, this implies that queries in LoqQL that filter based on log content require loading all corresponding blocks.</p>
<p>Key features of Loki include:</p>
<ul>
<li>Efficient memory usage for indexing logs.</li>
<li>Multitenancy.</li>
<li>LogQL, a query language similar to Prometheus’ PromQL, which allows generating metrics from log data.</li>
<li>Scalability, being able to operate both in a single binary and in microservices.</li>
</ul>
<p>Loki stands out for splitting queries into small parts and executing them in parallel to speed up the search in large volumes of data. Unlike other systems that require large full-text indexes, Loki’s index is significantly smaller than the volume of logs ingested, which keeps static costs low and allows controlling query performance through horizontal scaling.</p>
<p>Tags in Loki define a flow and any change in the value of a tag creates a new flow. High cardinality in tags (many tags or tags with wide range of values) can lead to a large number of different flows, which can slow down and make operation expensive.</p>
<h3>Modes</h3>
<p>Loki operates in different modes:</p>
<ul>
<li>Monolithic: all components in a single binary.</li>
<li>Simple scalable: separation between read and write.</li>
<li>Microservices: distribution in several services that can be deployed with Helm.</li>
</ul>
<p>:warning: Monolithic is only intended for development and testing, not for production.</p>
<h3>Components</h3>
<p>Loki components include:</p>
<ul>
<li>Distributor: validates and preprocesses log streams, rate limits and distributes them to ingestors.</li>
<li>Ingestor: writes log data to long-term storage and responds to in-memory queries.</li>
<li>Query Frontend (optional): optimizes queries, handling large loads and distributing them evenly.</li>
<li>Querier: handles queries using LogQL, fetching data from both ingestors and long-term storage.</li>
</ul>
<img src="https://blog.crstian.me/_astro/loki_architecture_components.omM7QHil_Z1YGs10.svg" alt="components_diagram" />
<h3>Architecture</h3>
<h4>Distributor</h4>
<p>The Distributor is responsible for handling incoming log streams, being the first stop on the write path. It performs validation of each stream to ensure its correctness and that it complies with the established limits. Valid chunks are split and sent to multiple ingesters. It is a stateless component, which facilitates scalability.</p>
<ul>
<li><strong>Reference</strong>: <a href="https://grafana.com/docs/loki/latest/architecture/#distributor" rel="noopener noreferrer" target="_blank">Distributor at Loki</a></li>
</ul>
<h4>Ingester</h4>
<p>The Ingester writes log data to long-term storage backends and returns log data for in-memory queries. It handles a lifecycle within the hash ring, with states such as <code>PENDING</code>, <code>JOINING</code>, <code>ACTIVE</code>, <code>LEAVING</code>, <code>UNHEALTHY</code>. Each log stream is accumulated in chunks in memory and then flushed to storage. It is key in data replication to mitigate data loss. On the write path, they temporarily store the logs in memory and periodically dump them to storage systems such as DynamoDB, S3, Cassandra, among others. In addition, on the read path, they return log data that is in their memory.</p>
<ul>
<li><strong>Reference</strong>: <a href="https://grafana.com/docs/loki/latest/architecture/#ingester" rel="noopener noreferrer" target="_blank">Ingester on Loki</a></li>
</ul>
<h4>Query Frontend</h4>
<p>The Query Frontend is an optional service that provides API endpoints for queriers and accelerates the read path. It performs query tuning and keeps queries in an internal queue. It works in conjunction with queriers, which act as workers. It is a stateless component, and it is recommended to run some replicas to benefit from fair scheduling.</p>
<ul>
<li><strong>Reference</strong>: <a href="https://grafana.com/docs/loki/latest/architecture/#query-frontend" rel="noopener noreferrer" target="_blank">Query Frontend in Loki</a></li>
</ul>
<h4>Querier</h4>
<p>Querier handles queries using the LogQL query language, fetching logs from both ingesters and long-term storage. It performs internal deduplication of data that have the same timestamp, tag set and log message. It plays a crucial role in the read path.</p>
<ul>
<li>Reference**: <a href="https://grafana.com/docs/loki/latest/architecture/#querier" rel="noopener noreferrer" target="_blank">Querier on Loki</a></li>
</ul>
<h4><strong>Index Gateway</strong></h4>
<p>If indexed storage, such as DynamoDB or Cassandra, is used for log indexes, the Index Gateway can be used to download and query indexes, thus reducing the number of direct queries to these indexed storage services.</p>
<h4><strong>Compactor</strong></h4>
<p>In configurations using indexed storage, the Compactor is responsible for compacting the indexes and improving their efficiency, both in terms of cost and query performance.</p>
<h4><strong>Ruler</strong></h4>
<p>This service is used for the evaluation of rules and alerts. It allows [[Loki]] to evaluate LogQL expressions on a regular schedule and execute actions based on the results of these evaluations.</p>
<h2>Write and Read Path</h2>
<h4>Write Path</h4>
<p>The write path manages how log data is collected, processed and stored. It involves the following steps and components:</p>
<ol>
<li>
<p><strong>Log Collection</strong>: It starts with log collection, usually through agents such as Promtail, Fluentd or Fluent Bit.</p>
</li>
<li>
<p><strong>Distributor</strong>: The collected logs are sent to the distributor. The distributor validates the logs and distributes them to the ingesters. The validation includes verifying the tags and the size and time limits of the logs.</p>
</li>
<li>
<p>**Ingester: The ingesters receive and temporarily store the logs in memory. The logs are organized in “chunks”.</p>
</li>
<li>
<p><strong>Long-Term Storage</strong>: Compressed chunks are stored in long-term storage systems.</p>
</li>
</ol>
<h4>Read Path</h4>
<p>The read path describes how stored log data is queried and retrieved. The steps and components involved are:</p>
<ol>
<li>
<p><strong>Query</strong>: Queries are generally performed through Grafana using the LogQL query language.</p>
</li>
<li>
<p><strong>Query Frontend</strong>: If in use, the query frontend receives the query and performs optimizations.</p>
</li>
<li>
<p><strong>Querier</strong>: The querier is the service that actually executes the query. It first queries the ingesters for recent data and then queries the long-term storage.</p>
</li>
<li>
<p><strong>Results</strong>: The results of the query are returned to the user.</p>
</li>
</ol>
<p>I hope it has helped you.</p>
<img src="https://blog.crstian.me/_astro/chibimon.rl7zZote_ZYy7ld.webp" alt="chibimon" />]]></content:encoded>
      <author>Crstian</author>
      <category>Loki</category><category>grafana</category><category>logs</category><category>kubernetes</category><category>monitoring</category><category>k8s</category>
    </item>
    <item>
      <title>Integrating the Salicru SPS 500 ONE UPS with Home Assistant using Docker</title>
      <link>https://blog.crstian.me/en/post/2022-08-05-integrating-sai-salicru-sps-500-one-in-home-assistant-with-docker/</link>
      <guid>https://blog.crstian.me/en/post/2022-08-05-integrating-sai-salicru-sps-500-one-in-home-assistant-with-docker/</guid>
      <updated>2022-08-05T00:00:00.000Z</updated>
      <pubDate>2022-08-05T00:00:00.000Z</pubDate>
      <description><![CDATA[How to integrate the Salicru SPS 500 ONE UPS with Home Assistant using Docker.]]></description>
      <content:encoded><![CDATA[
<p>How to integrate the Salicru SPS 500 ONE UPS with Home Assistant using Docker.</p>
<img src="https://blog.crstian.me/_astro/SalicruHASS.Bl0nFodT_1axWt1.webp" alt="Salicru SPS 500 ONE in Home Assistant" />
<p>Well, since practically all my services are self-hosted on my server, I decided to buy a UPS (Uninterruptible Power Supply) so that if the power goes out for any reason, the server won’t shut down abruptly and potentially damage a component, especially an HDD.</p>
<p>Since I only wanted it for the server and not much else, I chose the <a href="https://amzn.to/4lBd2kS" rel="noopener noreferrer" target="_blank">Salicru SPS 500 ONE</a>, which is more than enough for the server and anything else I might connect to it in the future.</p>
<p>Once I had it connected, I started looking into how to integrate it with Home Assistant. I had previously seen people who had integrated UPS systems into their HASS, so I began to investigate.</p>
<p>First, the obvious part: for the UPS to communicate with our Home Assistant, we need to connect the UPS via USB to the device where we have Home Assistant running.</p>
<p>For our Home Assistant to communicate with the UPS, we need NUT, a tool that acts as a bridge between the UPS and Home Assistant.</p>
<p>In our case, we are going to run NUT in Docker along with Home Assistant in the same docker-compose.yml.</p>
<h2>Detecting the New USB Device of the UPS</h2>
<p>We will execute the following command:</p>
<p>(We need to have usbutils installed)</p>
<pre><code># lsusb
</code></pre>
<img src="https://blog.crstian.me/_astro/lsusb.DZj1vjtT_2kKsMz.webp" alt="lsusb" />
<h1>Docker</h1>
<p>Once we have these two figures, we will substitute them into our compose file (the complete compose is later in the post).</p>
<pre><code>    devices:
      - /dev/bus/usb/001/003
</code></pre>
<p>Once we have this information, it’s time to deploy our two services in Docker:</p>
<p><strong>With Traefik</strong></p>
<pre><code>
version: '3.9'

services:

  homeassistant:
    image: homeassistant/home-assistant:latest
    restart: unless-stopped
    volumes:
      - /data/homeassistant:/config
    environment:
      - TZ=Europe/Madrid
    network_mode: host
    labels:
      - traefik.enable=true
      - traefik.http.routers.homeassistant.entryPoints=web-secure
      - traefik.http.routers.homeassistant.rule=Host(`homeassistant.crstian.me`)
      - traefik.http.services.homeassistant.loadbalancer.server.port=8123
      
  nut-upsd:
    container_name: nut-upsd
    image: upshift/nut-upsd
    restart: unless-stopped
    environment:
      - UPS_DRIVER=blazer_usb
    ports:
      - '3493:3493'
    devices:
      - /dev/bus/usb/001/003

</code></pre>
<p><strong>Without Traefik</strong></p>
<pre><code>
version: '3.9'

services:

  homeassistant:
    image: homeassistant/home-assistant:latest
    restart: unless-stopped
    volumes:
      - /data/homeassistant:/config
    environment:
      - TZ=Europe/Madrid
    ports:
      - '8123:8123'
    network_mode: host
      
  nut-upsd:
    container_name: nut-upsd
    image: upshift/nut-upsd
    restart: unless-stopped
    environment:
      - UPS_DRIVER=blazer_usb
    ports:
      - '3493:3493'
    devices:
      - /dev/bus/usb/001/003

</code></pre>
<h2>Important</h2>
<p>In my case, the UPS driver is “blazer_usb” but not all UPS devices use the same driver. To find out which driver you would need if you are not using an SPS 500, you can check <a href="https://networkupstools.org/stable-hcl.html" rel="noopener noreferrer" target="_blank">here</a> by entering your model which will tell you the required driver.</p>
<h1>Home Assistant</h1>
<p>Now let’s go to our Home Assistant to add our NUT:</p>
<p>We add a new NUT integration and set up the connection with ours.</p>
<p>Here are the default username and password; if you want to change them, you can add it as an environment variable in the compose. More info <a href="https://hub.docker.com/r/upshift/nut-upsd" rel="noopener noreferrer" target="_blank">here</a>.</p>
<img src="https://blog.crstian.me/_astro/NUTserver.D1ZJiziD_ZcaOPd.webp" alt="Home Assistant NUT" />
<p>And once added, we can set up a dashboard with all the information.</p>
<img src="https://blog.crstian.me/_astro/NUTDashboard.MJWLbHpz_Z1WPajt.webp" alt="Home Assistant NUT" />
<p>I hope this has been helpful!</p>
<img src="https://blog.crstian.me/_astro/totodile.kxRVzK_n_1qhBCn.webp" alt="Totodile gif" />]]></content:encoded>
      <author>Crstian</author>
      <category>SAI</category><category>Home Assistant</category><category>Salicru</category><category>HASS</category><category>SPS 500 ONE</category>
    </item>
    <item>
      <title>Configure DDNS in OpenWrt Cloudflare</title>
      <link>https://blog.crstian.me/en/post/2022-01-19-openwrt-ddns/</link>
      <guid>https://blog.crstian.me/en/post/2022-01-19-openwrt-ddns/</guid>
      <updated>2022-01-10T00:00:00.000Z</updated>
      <pubDate>2022-01-10T00:00:00.000Z</pubDate>
      <description><![CDATA[If you have services in your home as in my case, your company will probably give you a dynamic public IP, so it is a hassle to have to be changing by hand the…]]></description>
      <content:encoded><![CDATA[
<p>If you have services in your home as in my case, your company will probably give you a dynamic public IP, so it is a hassle to have to be changing by hand the IP where your domain points every time your company changes the IP, so let’s see how to configure our router with OpenWRT with Cloudflare as DNS in a very simple way.</p>
<p>List of routers that I recommended:</p>
<ul>
<li><a href="https://s.click.aliexpress.com/e/_omrrCEA" rel="noopener noreferrer" target="_blank">BPI-R4</a></li>
<li><a href="https://amzn.to/3GfwMLP" rel="noopener noreferrer" target="_blank">Linksys MR7350</a></li>
</ul>
<h2>Cloudflare Token</h2>
<p>First of all we will need a cloudflare token, we can get it from here <a href="https://dash.cloudflare.com/profile/api-tokens" rel="noopener noreferrer" target="_blank">https://dash.cloudflare.com/profile/api-tokens</a> with our account.</p>
<p>Create a new token with this configuration:</p>
<pre><code>Zone, Zone: Read
Zone, DNS: Edit
</code></pre>
<img src="https://blog.crstian.me/_astro/Cloudflare-Token.BG2xg4yK_Z2au7SR.webp" alt="" />
<h2>Configure OpenWRT</h2>
<p>Now we need to install the following packages (in my case via shell but it can also be done from Luci):</p>
<p><strong>For OpenWRT 19.07 we need to install ddns-scripts_cloudflare.com-v4</strong> <strong>For OpenWRT 20.02 or higher the package is ddns-scripts-cloudflare</strong>.</p>
<p><strong>For OpenWRT 20.02 or higher the package is ddns-scripts-cloudflare</strong>.</p>
<pre><code>opkg update
opkg install ddns-scripts-cloudflare luci-app-ddns
opkg install wget ca-certificates
opkg install curl ca-bundle

</code></pre>
<p>Once everything is installed we go to the router IP in the browser Services -&gt; Dynamic DNS.</p>
<p>Edit the IPv4:</p>
<ul>
<li>Select DDNS Service provider cloudflare.com-v4.</li>
<li>In Lookup Hostname we place our subdomain or domain for example in my case murcia.crstian.me, in Domain the same but in this format <a href="mailto:murcia@crstian.me">murcia@crstian.me</a></li>
<li>In username <strong>Bearer</strong> and in password our cloudflare token.</li>
</ul>
<img src="https://blog.crstian.me/_astro/Ddns-config.a9qOXK9q_Z58LiY.webp" alt="" />
<p>Once this is done, click on Save and Apply.</p>
<h2>Testing</h2>
<p>To see that it is working we can see it in Services -&gt; Dynamic DNS and we see how it takes our IP and if we go to the DNS of cloudflare we see that it has changed it.</p>
<img src="https://blog.crstian.me/_astro/Ddns-Done.B44kmqvb_r8lSo.webp" alt="" />]]></content:encoded>
      <author>Crstian</author>
      <category>OpenWRT</category><category>DDns</category><category>Cloudflare</category><category>Banana pi r4</category>
    </item>
    <item>
      <title>Using Drone CI/CD with Traefik on NixOS</title>
      <link>https://blog.crstian.me/en/post/2021-01-15-drone-ci/</link>
      <guid>https://blog.crstian.me/en/post/2021-01-15-drone-ci/</guid>
      <updated>2021-01-15T00:00:00.000Z</updated>
      <pubDate>2021-01-15T00:00:00.000Z</pubDate>
      <description><![CDATA[In this post we are going to see how to configure our own CI/CD environment with Drone, integrated with Traefik as reverse proxy and NixOS as operating system…]]></description>
      <content:encoded><![CDATA[
<p>In this post we are going to see how to configure our own CI/CD environment with <a href="https://www.drone.io/" rel="noopener noreferrer" target="_blank">Drone</a>, integrated with <a href="https://traefik.io/" rel="noopener noreferrer" target="_blank">Traefik</a> as reverse proxy and <a href="https://nixos.org/" rel="noopener noreferrer" target="_blank">NixOS</a> as operating system using github repositories.</p>
<div><img src="https://blog.crstian.me/_astro/NixOS.ghMtZV7Y_Z1qwBE.webp" alt="" /><img src="https://blog.crstian.me/_astro/Drone.BAfbC_Ry_18KkzH.webp" alt="" /></div><p>First we need to create an OAuth application in github and our RPC secret can be seen in this <a href="https://rubynor.com/blog/2020/06/setting-up-drone-ci-for-rails-apps/" rel="noopener noreferrer" target="_blank">post</a> or also in the <a href="https://docs.drone.io/server/provider/github/" rel="noopener noreferrer" target="_blank">drone docs</a>.</p>
<p>To create our RPC secret we will do the following:</p>
<blockquote><p>$ openssl rand -hex 16
bea26a2221fd8090ea38720fc445eca6</p></blockquote>
<p>Once we have our client ID and our client secret we proceed to create our docker-compose.yml to deploy our drone container.</p>
<pre><code>version: '3.7'

services:
  drone-server:
    container_name: drone-server
    image: drone/drone:1
    restart: unless-stopped
    volumes:
      - ./drone:/data
    environment:
      - DRONE_GITHUB_CLIENT_ID=&lt;Your_Client_ID&gt;
      - DRONE_GITHUB_CLIENT_SECRET=&lt;Your_Client_secret&gt;
      - DRONE_RPC_SECRET=&lt;Your_RPC_secret&gt;
      - DRONE_SERVER_HOST=&lt;Your_URL&gt;
      - DRONE_SERVER_PROTO=https
      - DRONE_USER_CREATE=username:&lt;Your_Github_username&gt;,admin:true
    labels:
      - traefik.enable=true
      - traefik.http.routers.drone.entryPoints=web-secure
      - traefik.http.routers.drone.rule=Host(`Your_URL`)
      - traefik.http.routers.drone.tls.certresolver=default

</code></pre>
<p>Then we deploy our container with:</p>
<blockquote><p>$ docker-compose up -d</p></blockquote>
<p>Once our server is up, we are going to run our host runner executor and for this we download the drone-exec binary from the drone web site:</p>
<blockquote><p>$ curl -L <a href="https://github.com/drone-runners/drone-runner-exec/releases/latest/download/drone_runner_exec_linux_amd64.tar.gz" rel="noopener noreferrer" target="_blank">https://github.com/drone-runners/drone-runner-exec/releases/latest/download/drone_runner_exec_linux_amd64.tar.gz</a> | tar zx</p></blockquote>
<p>Once downloaded we have to move it to /root/bin/ and then in the configuration.nix add the following:</p>
<pre><code>environment.etc.drone-runner-exec = {
  target = "drone-runner-exec/config";
  text = ''
  DRONE_RPC_PROTO=https
  DRONE_RPC_HOST=&lt;Your_URL&gt;
  DRONE_RPC_SECRET=&lt;Your_RPC_secret&gt;
  DRONE_UI_USERNAME=root
  DRONE_UI_PASSWORD=root
  '';
};

systemd.services.drone-runner-exec = {
  description = "Drone Exec Runner";
  startLimitIntervalSec = 5;
  serviceConfig = {
    ExecStart = "/root/bin/drone-runner-exec service run --config /etc/drone-runner-exec/config";
  };
  wantedBy = [ "multi-user.target" ];
  path = [ pkgs.git pkgs.docker pkgs.docker-compose ];
};
</code></pre>
<p>Next:</p>
<blockquote><p>nixos-rebuild switch</p></blockquote>
<p>Now we access our Drone URL and authenticate with our Github account.</p>
<img src="https://blog.crstian.me/_astro/GithubLogin.tUUD_s4z_Z1lPjGT.webp" alt="" />
<p>So far we have set up our infrastructure with Drone, from here on everyone can do what they need, in my case let’s see how I have it set up so that every time I create a post and push it to the github repo, the deployment is automatically done and the nginx container is updated with the modifications.</p>
<p>Once inside the web of our drone, we activate the repository that we want in my case the repository of this blog.</p>
<img src="https://blog.crstian.me/_astro/Dronepage.C4CeaiDS_Z2vnrgj.webp" alt="" />
<p>To perform the configuration our repository must have 3 essential files in the repository root:</p>
<p><a href="https://github.com/Crstian19/My-personal-blog/blob/Main/.drone.yml" rel="noopener noreferrer" target="_blank"><strong>.drone.yml</strong></a></p>
<p>In this file we basically configure the actions to be performed by drone in our case simply performs the docker build and then raises the container.</p>
<pre><code>kind: pipeline
type: exec
name: default

platform:
  os: linux
  arch: amd64

steps:
- name: build
  commands:
    - docker build -t bloglord .

- name: run
  commands:
    - docker-compose down -v
    - docker-compose up -d --build
</code></pre>
<p><a href="https://github.com/Crstian19/My-personal-blog/blob/Main/Dockerfile" rel="noopener noreferrer" target="_blank"><strong>Dockerfile</strong></a></p>
<p>Little to comment on this file, simply copy the contents of public in the html folder of the nginx container.</p>
<pre><code>FROM nginx:alpine
COPY public/ /usr/share/nginx/html
</code></pre>
<p><a href="https://github.com/Crstian19/My-personal-blog/blob/Main/docker-compose.yml" rel="noopener noreferrer" target="_blank"><strong>docker-compose.yml</strong></a></p>
<p>The compose of the container where we provide traefik labels.</p>
<pre><code>version: '3.7'

services:
  bloglord:
    image: bloglord
    container_name: blog
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.routers.bloglord.entryPoints=web-secure
      - traefik.http.routers.bloglord.rule=Host(`blog.crstian.me`)
      - traefik.http.routers.bloglord.tls.certresolver=default

networks:
  default:
    name: bloglord-network
</code></pre>
<p>Once all this is done we can check it by performing a commit where we will see how we have automated the modifications to the blog.</p>
<p>If something went wrong we could see it in the activity feed of each commit.</p>
<img src="https://blog.crstian.me/_astro/DroneActivityFeed.Zjje_z1a_NmWDO.webp" alt="" />
<p>And our Drone would be ready for everything we need.</p>
<img src="https://blog.crstian.me/_astro/f47a703b66a1d1e2a2f9b3078a00215a.Cdf0dURE_Z3wi5a.webp" alt="" />
<p>I hope you found it useful.</p>
<h3>Sources</h3>
<ul>
<li><a href="https://github.com/Raniita/app-drone-tests" rel="noopener noreferrer" target="_blank">https://github.com/Raniita/app-drone-tests</a></li>
<li><a href="https://rubynor.com/blog/2020/06/setting-up-drone-ci-for-rails-apps/" rel="noopener noreferrer" target="_blank">https://rubynor.com/blog/2020/06/setting-up-drone-ci-for-rails-apps/</a></li>
<li><a href="https://docs.drone.io/server/provider/github/" rel="noopener noreferrer" target="_blank">https://docs.drone.io/server/provider/github/</a></li>
</ul>]]></content:encoded>
      <author>Crstian</author>
      <category>Drone</category><category>CI</category><category>CD</category><category>Traefik</category><category>NixOS</category>
    </item>
    <item>
      <title>Secure routing for your containerized applications using Traefik v2 with Let&#039;s Encrypt</title>
      <link>https://blog.crstian.me/en/post/2020-04-19-secure-routing-for-your-containerized-applications-using-traefik-v2-with-lets-encrypt/</link>
      <guid>https://blog.crstian.me/en/post/2020-04-19-secure-routing-for-your-containerized-applications-using-traefik-v2-with-lets-encrypt/</guid>
      <updated>2020-04-19T00:00:00.000Z</updated>
      <pubDate>2020-04-19T00:00:00.000Z</pubDate>
      <description><![CDATA[Configuring traefik v2 for secure routing as reverse proxy with HTTPS]]></description>
      <content:encoded><![CDATA[
<p>Configuring traefik v2 for secure routing as reverse proxy with HTTPS</p>
<img src="https://blog.crstian.me/_astro/traefik-architecture.Bip_l5uk_1PcPQH.webp" alt="Traefik v2" />
<h2>Introduction</h2>
<p>A robust reverse proxy configuration is critical for any self-hosted setup that has elements exposed across the Internet. While reverse proxy services are often used for load balancing and security reasons, most home server owners use them to route requests directed to different domains or subdomains to different hosts or internal services. In my case you can see all my services at <a href="https://server.crstian.me/" rel="noopener noreferrer" target="_blank">https://server.crstian.me/</a>.</p>
<p>In this post we are going to see how to configure Traefik as a reverse proxy such as <a href="https://www.nginx.com/" rel="noopener noreferrer" target="_blank">NGINX</a>.</p>
<p>Let’s see how to securely route requests to a subdomain pointing to a specific port in a container, all securely through HTTPS.</p>
<h2>What is a reverse proxy?</h2>
<p>A reverse proxy is a type of proxy server that retrieves resources on behalf of a client from one or more servers. These resources are then returned to the client as if they originated from the Web server itself.</p>
<p>Which explained in a more vulgar way would be in our case to access different services that we have locally but from the outside and in this case Traefik does the routing work and depending on the subdomain it takes us to one service or another.
<img src="https://blog.crstian.me/_astro/services.DcosZ4n3_xEb5g.webp" alt="Traefik routers" /></p>
<h3>Load balancing</h3>
<p>A reverse proxy can distribute the load of incoming requests to multiple servers, with each server running its own application area.</p>
<p>Reverse proxies provide an additional layer of security by never revealing the IP address of the backend server that actually handles the requests. This means that attackers will normally only be able to attack the reverse proxy servers themselves.</p>
<h3>SSL encryption</h3>
<p>A reverse proxy can be configured with SSL encryption in order to automatically generate certificates for each route with <a href="https://letsencrypt.org/es/" rel="noopener noreferrer" target="_blank">Let’s Encrypt</a>.</p>
<h2>Configure Traefik v2</h2>
<p>In our case we are going to set it up with a <a href="https://docs.docker.com/compose/" rel="noopener noreferrer" target="_blank">docker-compose.yml</a></p>
<pre><code>version: '3.7'

services:
  traefik:
    image: traefik:latest
    network_mode: host
    volumes:
      - ./config/:/etc/traefik/
      - /var/run/docker.sock:/var/run/docker.sock
</code></pre>
<p>Now let’s configure Traefik with its configuration file traefik.toml.</p>
<pre><code>api:
  dashboard: true

entryPoints:
  web:
    address: ":80"
  web-secure:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: /etc/traefik/config.yml
    watch: true

certificatesResolvers:
  default:
    acme:
      email: your email
      storage: /etc/traefik/acme/acme.json
      tlsChallenge: {}
</code></pre>
<h3>Docker Integration</h3>
<p>Traefik uses the API of each <a href="https://docs.traefik.io/providers/overview/" rel="noopener noreferrer" target="_blank">provider</a> to find routing information and configure itself accordingly.</p>
<img src="https://blog.crstian.me/_astro/providers.C2h5IvoP_Z10iHwd.webp" alt="Traefik v2 providers" />
<h3>Entry points</h3>
<p>Entry points simply define the ports on which Traefik will listen to receive packets. Two entry points are configured here, web and websecure for ports 80 and 443.</p>
<h2>Container configuration</h2>
<p>The last step to expose our container using Traefik is to add some Docker tags that will allow Traefik to find it.</p>
<p>These are the necessary tags:</p>
<pre><code>labels:
      - traefik.enable=true
      - traefik.http.routers.yourservicename.entryPoints=web-secure
      - traefik.http.routers.yourservicename.rule=Host(`subdomain.your.domain`)
      - traefik.http.routers.yourservicename.tls=true
      - traefik.http.routers.yourservicename.middlewares=user:password
      - traefik.http.services.yourservicename.loadbalancer.server.port=serviceport    
</code></pre>
<ul>
<li>
<p><code>yourservicename</code> tenemos que cambiarlo por el nombre de nuestra aplicación, por ejemplo <code>netdata</code></p>
</li>
<li>
<p><code>subdomain.your.domain</code> tenemos que cambiarlo por nuestro subdominio que queremos que apunte a nuestra aplicación, por ejemplo <code>netdata.crstian.me</code></p>
</li>
<li>
<p><code>serviceport</code> aquí tenemos que cambiarlo por el puerto que use nuestro servicio</p>
</li>
<li>
<p><code>user:password</code> en caso de que queramos ponerle usuario y contraseña para entrar a ese servicio debemos usar usuario y contraseña como si fuera htaccess</p>
</li>
</ul>
<p>An example of a service I have running on my server with its docker-compose.yml</p>
<pre><code>version: '3'
services:
  caddy:
    image: abiosoft/caddy
    volumes:
      - '/storage/shared:/srv'
    labels:
      - traefik.enable=true
      - traefik.http.routers.caddy.entryPoints=web-secure
      - traefik.http.routers.caddy.rule=Host(`downloads.crstian.me`)
      - traefik.http.routers.caddy.tls.certresolver=default
      - traefik.http.services.caddy.loadbalancer.server.port=2015
      - traefik.http.routers.caddy.middlewares=torrent
      - traefik.http.middlewares.torrent.basicAuth.users=mypasswordbro
    restart: unless-stopped
</code></pre>
<h2>Traefik Dashboard</h2>
<p>Finally we are going to configure inside traefik itself so that we can access its dashboard through a subdomain.</p>
<p>Inside the config.yml file we have to have the following:</p>
<pre><code>traefik:
      rule: Host(`traefik.crstian.me`)
      entryPoints:
        - "web-secure"
      service: api@internal
      middlewares:
        - auth
      tls:
        certResolver: default
</code></pre>
<p>It should show this dashboard</p>
<img src="https://blog.crstian.me/_astro/traefikdashboard.ckgdNBMf_Z1R89vl.webp" alt="Traefik v2" />]]></content:encoded>
      <author>Crstian</author>
      <category>Traefik</category><category>Reverse Proxy</category>
    </item>
    <item>
      <title>Logitech G502 Linux</title>
      <link>https://blog.crstian.me/en/post/2019-07-28-logitech-g502-linux/</link>
      <guid>https://blog.crstian.me/en/post/2019-07-28-logitech-g502-linux/</guid>
      <updated>2019-07-28T00:00:00.000Z</updated>
      <pubDate>2019-07-28T00:00:00.000Z</pubDate>
      <description><![CDATA[How to configure and use Logitech G502 on Arch Linux]]></description>
      <content:encoded><![CDATA[
<p>How to configure and use Logitech G502 on Arch Linux</p>
<h2>How to configure and use Logitech G502 on Arch Linux</h2>
<p>After switching to Arch Linux from Windows and having a Logitech G502 I needed to configure both dpi and configurable buttons, even turn off the leds at night.</p>
<p>Thanks to a friend who taught me <a href="https://github.com/libratbag/piper" rel="noopener noreferrer" target="_blank">piper</a> program made in Python which allows us to configure our G502 and more models <a href="https://github.com/libratbag/libratbag/tree/master/data/devices" rel="noopener noreferrer" target="_blank">here the list </a></p>
<p>In my case using Arch Linux I already had the package in the <a href="https://aur.archlinux.org/" rel="noopener noreferrer" target="_blank">AUR</a>, but in the github repository of piper we have explained how to install it <a href="https://github.com/libratbag/piper/wiki/Installation" rel="noopener noreferrer" target="_blank">Wiki</a></p>
<p>Once installed it is as easy as launching the program.</p>
<img src="https://blog.crstian.me/_astro/Resolution.CqTcvEVB_Z1BEpbx.webp" alt="" />
<p>In this screen we can modify the dpi and sensitivity.</p>
<img src="https://blog.crstian.me/_astro/Buttons.BG7SmfHq_Z29Atsp.webp" alt="" />
<p>In this we can change the functions of the different buttons.</p>
<img src="https://blog.crstian.me/_astro/Leds.WtExm3A1_Z1eCnJY.webp" alt="" />
<p>Finally here we can modify the leds.</p>
<p>To apply the changes we simply have to push the right corner “Apply”.</p>]]></content:encoded>
      <author>Crstian</author>
      <category>Logitech G502</category><category>Linux</category>
    </item>
    <item>
      <title>Github for noobies 🤔</title>
      <link>https://blog.crstian.me/en/post/2019-07-17-git-tutorial/</link>
      <guid>https://blog.crstian.me/en/post/2019-07-17-git-tutorial/</guid>
      <updated>2019-07-17T00:00:00.000Z</updated>
      <pubDate>2019-07-17T00:00:00.000Z</pubDate>
      <description><![CDATA[In this post we are going to see how to create our first repository and have our code ordered in Github. We are going to learn the basics and the necessary…]]></description>
      <content:encoded><![CDATA[
<p>In this post we are going to see how to create our first repository and have our code ordered in Github. We are going to learn the basics and the necessary commands for it, that’s why I have titled it github for noobies or dummies like me.</p>
<h2>Git is the most popular version control system.</h2>
<p>Git is the most popular version control system and for good reason. In this post, we will go over the basics of what git is and how to use it inside the command line. We will examine and work with all the basic and most important commands, such as add, commit, push…</p>
<p>Git is an essential version control technology for developers and programmers.</p>
<img src="https://blog.crstian.me/_astro/1-OY34A4uBsawmGoqpBV3UaA.BGqdAYcA_1BSosV.webp" alt="Image result for git" />
<p>A version control system allows us to track the history of a collection of files. These versions are stored in a specific location, usually called a repository.</p>
<h2>Prerequisites</h2>
<p>If you have a GitHub account and have installed and configured Git on your computer, skip to the next step.</p>
<p>If you don’t have a GitHub account, go <a href="https://github.com/join" rel="noopener noreferrer" target="_blank"><strong>here</strong></a> and sign up for a free one. See how to install and configure Git <a href="https://help.github.com/articles/set-up-git/" rel="noopener noreferrer" target="_blank"><strong>here</strong></a>. We need the tool with terminal in case we are on windows. Follow the links to download and install it.</p>
<p>You can check if Git is installed using the following command:</p>
<blockquote><p>git—version</p></blockquote>
<h2>Creating our first repository</h2>
<p>In our pc we are going to create a folder for our project.
We will call it for example demo-git.</p>
<p>Using the command console we move to that folder and add a local repository using the following command:</p>
<blockquote><p>git init</p></blockquote>
<img src="https://blog.crstian.me/_astro/gitinit.DZV9HVBI_Z2obwJT.webp" alt="1800x300" />
<p>With this command we simply initialize an empty local repository, now our folder is initialized as a Git repository and we can do all the git stuff in it.</p>
<h2>Adding files to our local repository</h2>
<p>After we have been working on our project and created some files, it is time to prepare those files for uploading.</p>
<p>For example we are going to add the files named index.html and style.css.</p>
<p>To add each file separately we use the following command:</p>
<blockquote><p>git add index.html</p></blockquote>
<p>In case you want to add both files at the same time:</p>
<blockquote><p>git add index.html style.css</p></blockquote>
<p>Finally, if you want to add all the files contained in the folder:</p>
<blockquote><p>git add .</p></blockquote>
<p>To see the files we have added we can use the following command:</p>
<blockquote><p>git status</p></blockquote>
<img src="https://blog.crstian.me/_astro/gitadd.fPJjHo5-_2g6lLd.webp" alt="gitadd.png" />
<h2>“Commit” the changes</h2>
<p>After adding those files to the staging area, we can create a checkpoint called a commit. Each commit is usually created after we have finished working on a certain feature or fixing a certain bug. These commits can be used to control which features were added in which commits and by whom, so we can easily control or even revert the whole commit if there are any bugs in the code.</p>
<p>A commit in a repository could be said to be like a giant copy and paste, but even better!</p>
<p>To add a commit we use the following command:</p>
<blockquote><p>git commit -m “commit message”</p></blockquote>
<p>The -m means that we are going to add a certain message.</p>
<img src="https://blog.crstian.me/_astro/gitcommit.CyLQI-SE_ZQdyJR.webp" alt="gitcommit.png" />
<p>In this example, we are going to create our first commit, we are going to make a commit with the message “First commit”.</p>
<p>We can do this by typing git commit -m “First commit”.</p>
<p>The commit message should indicate what code changes were made in that particular commit.</p>
<h2>“Pushing” your changes to your master branch</h2>
<p>When all the files are added and committed, the only thing left to do is to “push/upload” them to our remote repository.</p>
<p>But before that we have to so to speak link our local repository to our github repository with the following command:</p>
<blockquote><p>git remote add origin (Link de nuestro repo)</p></blockquote>
<p>The first time we push files to a remote directory, we have to execute the following command:</p>
<blockquote><p>git push -u origin master</p></blockquote>
<img src="https://blog.crstian.me/_astro/gitpush.y_KKPcwK_Z2vWWjO.webp" alt="gitpush.png" />
<p>From the second push we will only have to execute the following:</p>
<blockquote><p>git push</p></blockquote>
<p>Some commands that may also be useful:</p>
<p>List all branches</p>
<blockquote><p>git branch</p></blockquote>
<p>Switching between different branches</p>
<blockquote><p>git checkout -b </p></blockquote>
<p>Revert last commit changes</p>
<blockquote><p>git revert</p></blockquote>
<h3>That’s all!</h3>
<p>I hope it served as an introduction to create your first repository 😉.</p>]]></content:encoded>
      <author>Crstian</author>
      <category>Git</category><category>Github</category>
    </item>
  </channel>
</rss>