<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Konrad Kądzielawa</title>
    <description>The latest articles on DEV Community by Konrad Kądzielawa (@kkadzielawa).</description>
    <link>https://dev.to/kkadzielawa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1223741%2F3be6d8be-1643-4359-b458-4795630af2c3.jpg</url>
      <title>DEV Community: Konrad Kądzielawa</title>
      <link>https://dev.to/kkadzielawa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kkadzielawa"/>
    <language>en</language>
    <item>
      <title>Building a Local IT Assistant with Ollama, Open WebUI and a Custom Model Router</title>
      <dc:creator>Konrad Kądzielawa</dc:creator>
      <pubDate>Mon, 29 Jun 2026 07:42:42 +0000</pubDate>
      <link>https://dev.to/kkadzielawa/building-a-local-it-assistant-with-ollama-open-webui-and-a-custom-model-router-42dp</link>
      <guid>https://dev.to/kkadzielawa/building-a-local-it-assistant-with-ollama-open-webui-and-a-custom-model-router-42dp</guid>
      <description>&lt;p&gt;Running a local LLM is easy. Turning it into something useful for daily IT administration is a different story.&lt;/p&gt;

&lt;p&gt;This article is not only about installing Ollama and Open WebUI in Docker. There are already many tutorials for that. What I wanted to build was a local assistant that could actually help with SysOps tasks: reading logs, explaining errors, preparing safe Bash commands, writing Ansible playbooks, reviewing YAML files, and helping with Docker-related troubleshooting.&lt;/p&gt;

&lt;p&gt;My goal was to prepare a local AI setup that could later be moved to a dedicated Actina machine and used as a small internal IT assistant.&lt;/p&gt;

&lt;p&gt;The interesting part was not the installation itself.&lt;/p&gt;

&lt;p&gt;The interesting part was what happened after the installation.&lt;/p&gt;

&lt;p&gt;I had to solve a few practical problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to choose a default model,&lt;/li&gt;
&lt;li&gt;how to route coding-related prompts to a better model,&lt;/li&gt;
&lt;li&gt;how to avoid manually switching models in the UI,&lt;/li&gt;
&lt;li&gt;how to make Open WebUI start with the right assistant,&lt;/li&gt;
&lt;li&gt;how to make the whole setup survive a VM restart,&lt;/li&gt;
&lt;li&gt;and how to keep the models and data persistent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is Part 1 of the setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final goal
&lt;/h2&gt;

&lt;p&gt;The final idea was simple:&lt;/p&gt;

&lt;p&gt;I wanted to open Open WebUI, select one assistant, and use it for IT administration.&lt;/p&gt;

&lt;p&gt;Under the hood, however, I wanted different models to be used depending on the task.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;General chat, logs, CSV analysis, explanations  -&amp;gt; qwen3:8b
Bash, Python, Docker, Ansible, YAML, errors     -&amp;gt; qwen2.5-coder:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So instead of manually switching between models, I created a custom Pipe Function in Open WebUI.&lt;/p&gt;

&lt;p&gt;From the user perspective, it looked like one model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AUTO Local IT Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But internally it worked as a simple model router.&lt;/p&gt;




&lt;h2&gt;
  
  
  Environment overview
&lt;/h2&gt;

&lt;p&gt;The local setup was based on Docker Compose.&lt;/p&gt;

&lt;p&gt;The project directory looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/kkadzielawa/ai-local
├── docker-compose.yml
├── .env
└── data
    ├── ollama
    └── open-webui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main containers were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama
open-webui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local models were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen3:8b
qwen2.5-coder:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen3:8b           -&amp;gt; general local IT assistant
qwen2.5-coder:7b  -&amp;gt; coding, scripting, Docker, Ansible, YAML
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, I thought that setting a default model would be enough.&lt;/p&gt;

&lt;p&gt;It was not.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 1: A default model is not enough
&lt;/h2&gt;

&lt;p&gt;The first approach was to configure Open WebUI with a default model.&lt;/p&gt;

&lt;p&gt;In Docker Compose, this can be done using environment variables like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_MODELS=qwen3:8b&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_PINNED_MODELS=qwen3:8b,qwen2.5-coder:7b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but only solves one problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which model should be selected by default?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not solve the more important problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which model is best for this specific prompt?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, this prompt is probably fine for a general model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explain what should be checked before upgrading a Debian server in production.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this one should go to a coding-oriented model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write an Ansible playbook that checks hostname, uptime, OS version and free disk space.
Read-only tasks only. Do not modify the system.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did not want to remember which model to select every time.&lt;/p&gt;

&lt;p&gt;I wanted Open WebUI to make that decision automatically.&lt;/p&gt;

&lt;p&gt;So I needed a custom layer between the user prompt and the actual model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 2: Tools, Pipelines or Functions?
&lt;/h2&gt;

&lt;p&gt;This was one of the first confusing parts.&lt;/p&gt;

&lt;p&gt;Open WebUI has several places that may look similar at first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Workspace -&amp;gt; Tools
Settings  -&amp;gt; Pipelines
Admin     -&amp;gt; Functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My first assumption was that this kind of logic should be created as a tool or pipeline.&lt;/p&gt;

&lt;p&gt;But that was not the right direction.&lt;/p&gt;

&lt;p&gt;A model router is not a normal tool called by the model during a conversation. It is not something like a calculator, web search or custom API tool.&lt;/p&gt;

&lt;p&gt;What I needed was something that behaves like a model in the UI, receives the user request, decides what to do with it, and then forwards it to the selected real model.&lt;/p&gt;

&lt;p&gt;The right place was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Admin Settings
-&amp;gt; Functions
-&amp;gt; New Function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the right type of function was a Pipe Function.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 3: Open WebUI showed an Example Filter, but I needed a Pipe
&lt;/h2&gt;

&lt;p&gt;When creating a new function, Open WebUI may show an example based on a filter.&lt;/p&gt;

&lt;p&gt;That can be misleading.&lt;/p&gt;

&lt;p&gt;For this use case, a filter was not what I needed.&lt;/p&gt;

&lt;p&gt;A filter can modify input or output, but I wanted to create something that appears as a selectable model and routes the request to another model.&lt;/p&gt;

&lt;p&gt;So the function had to be based on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pipe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was an important detail.&lt;/p&gt;

&lt;p&gt;The custom function was named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AUTO Local IT Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function had its own configurable valves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Default Model: qwen3:8b
Coder Model: qwen2.5-coder:7b
Show Selected Model: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allowed me to change the underlying models later without rewriting the whole function.&lt;/p&gt;




&lt;h2&gt;
  
  
  The routing logic
&lt;/h2&gt;

&lt;p&gt;The first version of the router was intentionally simple.&lt;/p&gt;

&lt;p&gt;It checked the user message and looked for keywords or patterns suggesting that the prompt was related to coding, scripting or infrastructure automation.&lt;/p&gt;

&lt;p&gt;Examples of keywords:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible
playbook
yaml
bash
python
powershell
docker compose
terraform
traceback
exception
stack trace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples of patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker ...
systemctl
journalctl
apt install
def function_name(...)
class Something
#!/bin/bash
- hosts:
tasks:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the prompt matched coding or automation-related patterns, the function routed it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen2.5-coder:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, it used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen3:8b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not an advanced agent.&lt;/p&gt;

&lt;p&gt;It is a simple and predictable router.&lt;/p&gt;

&lt;p&gt;And that was exactly what I wanted at this stage.&lt;/p&gt;

&lt;p&gt;For a local IT assistant, predictable behavior is often better than clever behavior that is hard to debug.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding a safety-oriented system prompt
&lt;/h2&gt;

&lt;p&gt;Because this assistant is meant for IT administration, I did not want it to randomly suggest destructive commands.&lt;/p&gt;

&lt;p&gt;So I added a system prompt with practical safety rules.&lt;/p&gt;

&lt;p&gt;The assistant should:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- answer in Polish,
- classify commands as read-only, system-changing or risky,
- suggest diagnostics before making production changes,
- avoid destructive commands without backups,
- explain Bash, Python and Ansible snippets,
- prefer safe troubleshooting steps first.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a very small addition, but it changes the behavior a lot.&lt;/p&gt;

&lt;p&gt;For example, instead of immediately suggesting a dangerous command, the assistant should first propose read-only diagnostics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
free &lt;span class="nt"&gt;-m&lt;/span&gt;
&lt;span class="nb"&gt;uptime
&lt;/span&gt;systemctl status docker
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; docker &lt;span class="nt"&gt;--no-pager&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For me, this is one of the most important parts of building a local SysOps assistant.&lt;/p&gt;

&lt;p&gt;The model should not only answer.&lt;/p&gt;

&lt;p&gt;It should answer in a way that matches how administrators actually work with production systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 4: The router existed, but Open WebUI still showed qwen3
&lt;/h2&gt;

&lt;p&gt;After creating the Pipe Function, I wanted Open WebUI to start with the router selected by default.&lt;/p&gt;

&lt;p&gt;So I changed the Docker Compose environment variables to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_MODELS=auto-local-it-assistant&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_PINNED_MODELS=auto-local-it-assistant,qwen3:8b,qwen2.5-coder:7b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, it looked like it did not work.&lt;/p&gt;

&lt;p&gt;Open WebUI still showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen3:8b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was confusing, because the configuration seemed correct.&lt;/p&gt;

&lt;p&gt;The reason was simple but easy to miss:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open WebUI can remember the model selected in an existing chat or user session.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the fix was not another Docker Compose change.&lt;/p&gt;

&lt;p&gt;The fix was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Hard refresh the browser with CTRL + F5
2. Start a new chat
3. Search for "auto" in the model selector
4. Select AUTO Local IT Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, the router worked as expected.&lt;/p&gt;

&lt;p&gt;The lesson here was important:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFAULT_MODELS affects the default state,
but existing chats or user sessions may still remember the old model.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a very practical troubleshooting point.&lt;/p&gt;

&lt;p&gt;Without knowing this, it is easy to waste time restarting containers, changing YAML files or assuming the Pipe Function is broken.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 5: Containers must start after VM reboot
&lt;/h2&gt;

&lt;p&gt;Another issue was more operational.&lt;/p&gt;

&lt;p&gt;This setup was running on a VM, and I did not want the local assistant to disappear after a reboot.&lt;/p&gt;

&lt;p&gt;The solution had two parts.&lt;/p&gt;

&lt;p&gt;First, Docker and containerd should start with the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker.service
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;containerd.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, the containers should have a proper restart policy.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ollama&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ollama/ollama:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ollama&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data/ollama:/root/.ollama&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="na"&gt;open-webui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/open-webui/open-webui:main&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;open-webui&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data/open-webui:/app/backend/data&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;restart: unless-stopped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;restart: always
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because I prefer this behavior for a local admin tool.&lt;/p&gt;

&lt;p&gt;If I manually stop the container, I usually mean it. I do not want Docker to bring it back immediately against my decision.&lt;/p&gt;

&lt;p&gt;To verify the restart policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.HostConfig.RestartPolicy.Name}}'&lt;/span&gt; ollama
docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.HostConfig.RestartPolicy.Name}}'&lt;/span&gt; open-webui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unless-stopped
unless-stopped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the real test:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;After the VM comes back:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Both containers should be running again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 6: Models and data must survive container recreation
&lt;/h2&gt;

&lt;p&gt;Another thing that matters a lot with Ollama and Open WebUI is persistence.&lt;/p&gt;

&lt;p&gt;I did not want downloaded models or Open WebUI data to live only inside containers.&lt;/p&gt;

&lt;p&gt;That is why I used local directories mounted into the containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data/ollama:/root/.ollama&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data/open-webui:/app/backend/data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives me a clean project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/kkadzielawa/ai-local
├── docker-compose.yml
├── .env
└── data
    ├── ollama
    └── open-webui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also makes the setup easier to back up or move to another machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; ai-local-backup-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz ai-local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was important because the environment was prepared with a future migration in mind.&lt;/p&gt;

&lt;p&gt;The VM was only the first step.&lt;/p&gt;

&lt;p&gt;The target was a dedicated machine for local AI-assisted administration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing the assistant
&lt;/h2&gt;

&lt;p&gt;I tested the router with a few different prompts.&lt;/p&gt;

&lt;p&gt;A general SysOps question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explain what should be checked before upgrading a Debian server in production.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen3:8b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Bash-related prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write a Bash script that checks CPU, RAM, disks, IP addresses and whether Docker is running.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen2.5-coder:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An Ansible prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write an Ansible playbook that checks hostname, uptime, OS version and free disk space.
Read-only tasks only.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen2.5-coder:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A YAML troubleshooting prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Check this docker-compose.yml and explain why Open WebUI cannot reach Ollama.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen2.5-coder:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A log analysis prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analyze this log and tell me what looks suspicious.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen3:8b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, the routing does not need to be perfect.&lt;/p&gt;

&lt;p&gt;It needs to be predictable and useful.&lt;/p&gt;

&lt;p&gt;That is enough for Part 1.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;The biggest lesson from this setup is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local AI is not only about running a model.
It is about building the workflow around the model.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ollama and Open WebUI give you a very good starting point.&lt;/p&gt;

&lt;p&gt;But the real value starts when you customize the environment for your own use case.&lt;/p&gt;

&lt;p&gt;In my case, the most important improvements were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- one visible assistant in the UI,
- automatic routing between a general model and a coding model,
- a safety-oriented system prompt,
- persistent model and Open WebUI data,
- container autostart after VM reboot,
- a structure that can be moved to another machine later.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most useful parts were not the obvious installation steps.&lt;/p&gt;

&lt;p&gt;The most useful parts were the small issues found along the way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- a model router is not a normal Tool,
- the correct Open WebUI feature is a Function,
- the function should be a Pipe, not a Filter,
- Open WebUI may remember the previous model in an existing chat,
- DEFAULT_MODELS may look broken until a new chat is created,
- restart policies should be verified, not assumed,
- persistent volumes are mandatory if the setup is meant to survive recreation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the details that usually do not appear in a clean installation guide.&lt;/p&gt;

&lt;p&gt;But these are exactly the details that matter when building something usable.&lt;/p&gt;




&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;This is only the first part of the project.&lt;/p&gt;

&lt;p&gt;The current setup gives me a local IT assistant with simple model routing.&lt;/p&gt;

&lt;p&gt;But there are many possible next steps.&lt;/p&gt;

&lt;p&gt;The most obvious one is adding a vision model.&lt;/p&gt;

&lt;p&gt;That would allow the assistant to handle screenshots, UI errors, diagrams or monitoring views.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screenshot or image -&amp;gt; vision model
code or automation  -&amp;gt; coder model
general IT question -&amp;gt; general model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another important step is RAG.&lt;/p&gt;

&lt;p&gt;I want the assistant to work with local documentation, internal notes, procedures, PDFs, CSV exports and troubleshooting guides.&lt;/p&gt;

&lt;p&gt;That would require adding an embedding model and a local knowledge base.&lt;/p&gt;

&lt;p&gt;Possible future improvements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Add a vision model for screenshots and UI troubleshooting
2. Add a local knowledge base with RAG
3. Add an embedding model for document search
4. Improve the routing logic
5. Add monitoring for Ollama and Open WebUI
6. Add backups for models and Open WebUI data
7. Add reverse proxy and HTTPS
8. Restrict access to VPN or internal LAN only
9. Build more specialized assistants for specific IT tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this is not a finished platform.&lt;/p&gt;

&lt;p&gt;It is the base layer.&lt;/p&gt;

&lt;p&gt;The first working version of a local SysOps assistant.&lt;/p&gt;

&lt;p&gt;And for me, this is the most interesting part of local AI: not just running a model, but slowly turning it into a practical internal tool.&lt;/p&gt;

</description>
      <category>pgaichallenge</category>
      <category>devops</category>
      <category>selfhosted</category>
      <category>ai</category>
    </item>
    <item>
      <title>Post-Mortem: Diagnosing and Fixing Ceph Quorum Breakdown (Time Drift) in a Hybrid Proxmox VE Cluster</title>
      <dc:creator>Konrad Kądzielawa</dc:creator>
      <pubDate>Wed, 25 Mar 2026 21:42:53 +0000</pubDate>
      <link>https://dev.to/kkadzielawa/post-mortem-diagnosing-and-fixing-ceph-quorum-breakdown-time-drift-in-a-hybrid-proxmox-ve-cluster-3d3d</link>
      <guid>https://dev.to/kkadzielawa/post-mortem-diagnosing-and-fixing-ceph-quorum-breakdown-time-drift-in-a-hybrid-proxmox-ve-cluster-3d3d</guid>
      <description>&lt;p&gt;&lt;strong&gt;Key Takeaways &amp;amp; Conclusions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Root Cause:&lt;/strong&gt; A Time Drift between the physical Proxmox host and nested/external virtual machines exceeded Ceph's strict &lt;strong&gt;0.05s&lt;/strong&gt; tolerance limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impact:&lt;/strong&gt; The time mismatch caused a continuous authorization loop (&lt;code&gt;electing&lt;/code&gt;/&lt;code&gt;probing&lt;/code&gt;) in the local Ceph Monitor (MON), corrupting its local RocksDB database and triggering a cascading failure of the dependent &lt;strong&gt;OSD.0&lt;/strong&gt; daemon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolution:&lt;/strong&gt; Enforced rigorous time synchronization via &lt;strong&gt;Chrony&lt;/strong&gt; and &lt;strong&gt;QEMU Guest Agent&lt;/strong&gt;, followed by a destructive purge and rebuild of the corrupted MON daemon using the surviving quorum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification:&lt;/strong&gt; Chaos Engineering tests (&lt;code&gt;rados bench&lt;/code&gt;) confirmed that with a &lt;strong&gt;min_size = 2&lt;/strong&gt; replication policy, the cluster successfully survives a hard node power-off without dropping client I/O.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. The Architecture and The Incident
&lt;/h2&gt;

&lt;p&gt;Building a Ceph cluster typically requires dedicated bare-metal servers. My home lab topology, however, is a hybrid 3-node setup designed to simulate a full cluster using limited hardware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;admin&lt;/strong&gt; (Physical Proxmox host, IP: &lt;code&gt;192.168.0.250&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;admin-02&lt;/strong&gt; (Nested VM 103 on the main host, IP: &lt;code&gt;192.168.0.251&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;admin-03&lt;/strong&gt; (External VM running on a separate Ubuntu desktop, IP: &lt;code&gt;192.168.0.252&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The incident began when the cluster state dropped to &lt;code&gt;HEALTH_WARN&lt;/code&gt;. Despite the underlying Corosync cluster and network layer (ICMP) functioning perfectly across the &lt;code&gt;192.168.0.0/24&lt;/code&gt; subnet, the main physical node (&lt;code&gt;admin&lt;/code&gt;) dropped out of the Ceph quorum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# ceph &lt;span class="nt"&gt;-s&lt;/span&gt;
  cluster:
    &lt;span class="nb"&gt;id&lt;/span&gt;:     25f2d0a2-d64b-42a2-b93d-99a560571e0e
    health: HEALTH_WARN
            1/3 mons down, quorum admin-02,admin-03
            1 osds down
            1 host &lt;span class="o"&gt;(&lt;/span&gt;1 osds&lt;span class="o"&gt;)&lt;/span&gt; down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Debugging &amp;amp; Log Analysis
&lt;/h2&gt;

&lt;p&gt;To find the root cause, I inspected the &lt;code&gt;systemd&lt;/code&gt; journals for the failed Object Storage Daemon (&lt;code&gt;osd.0&lt;/code&gt;) located on the physical &lt;code&gt;admin&lt;/code&gt; host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; ceph-osd@0.service &lt;span class="nt"&gt;-n&lt;/span&gt; 50 &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
Mar 25 21:08:21 admin ceph-osd[3147]: failed to fetch mon config &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--no-mon-config&lt;/span&gt; to skip&lt;span class="o"&gt;)&lt;/span&gt;
Mar 25 21:08:21 admin systemd[1]: ceph-osd@0.service: Main process exited, &lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited, &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1/FAILURE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OSD drive itself was physically healthy, but it crashed on startup because it could not retrieve the &lt;a href="https://www.google.com/search?q=https://docs.ceph.com/en/latest/architecture/%23cluster-map" rel="noopener noreferrer"&gt;Cluster Map&lt;/a&gt; from the local Monitor (&lt;code&gt;ceph-mon&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Next, I checked the local Monitor service on the &lt;code&gt;admin&lt;/code&gt; node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; ceph-mon@admin.service &lt;span class="nt"&gt;-n&lt;/span&gt; 30 &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
Mar 25 21:49:55 admin ceph-mon[1377]: 2026-03-25T21:49:55.526+0100 7c00b3b756c0 &lt;span class="nt"&gt;-1&lt;/span&gt; mon.admin@0&lt;span class="o"&gt;(&lt;/span&gt;probing&lt;span class="o"&gt;)&lt;/span&gt; e3 get_health_metrics reporting 1 slow ops, oldest is auth&lt;span class="o"&gt;(&lt;/span&gt;proto 0 30 bytes epoch 0&lt;span class="o"&gt;)&lt;/span&gt;
Mar 25 21:50:50 admin ceph-mon[1377]: 2026-03-25T21:50:50.528+0100 7c00b3b756c0 &lt;span class="nt"&gt;-1&lt;/span&gt; mon.admin@0&lt;span class="o"&gt;(&lt;/span&gt;electing&lt;span class="o"&gt;)&lt;/span&gt; e3 get_health_metrics reporting 1 slow ops, oldest is auth&lt;span class="o"&gt;(&lt;/span&gt;proto 0 30 bytes epoch 0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logs revealed the local MON was stuck in an endless &lt;code&gt;electing&lt;/code&gt;/&lt;code&gt;probing&lt;/code&gt; state, constantly dropping cryptographic authorization packets (&lt;code&gt;auth slow ops&lt;/code&gt;). According to &lt;a href="https://www.google.com/search?q=https://docs.ceph.com/en/latest/rados/configuration/mon-config-ref/%23clock" rel="noopener noreferrer"&gt;Ceph's official clock synchronization documentation&lt;/a&gt;, Monitors require clocks to be synchronized within &lt;strong&gt;0.05 seconds&lt;/strong&gt;. Because &lt;code&gt;admin-02&lt;/code&gt; and &lt;code&gt;admin-03&lt;/code&gt; were virtual machines, their CPU clock cycles drifted away from the physical host's hardware clock, effectively locking the &lt;code&gt;admin&lt;/code&gt; node out of the quorum and corrupting its metadata database.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Action Plan: Fixing Time Drift &amp;amp; Rebuilding
&lt;/h2&gt;

&lt;p&gt;To stabilize the cluster, I had to enforce strict timekeeping on the virtual nodes and reconstruct the corrupted Monitor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table: Remediation Steps per Node&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;th&gt;Ceph Role&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;th&gt;Implemented Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;admin&lt;/strong&gt; (Physical)&lt;/td&gt;
&lt;td&gt;MON, OSD.0&lt;/td&gt;
&lt;td&gt;Corrupted RocksDB&lt;/td&gt;
&lt;td&gt;Destroyed and recreated local MON daemon.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;admin-02&lt;/strong&gt; (VM &lt;strong&gt;103&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;MON, OSD.1&lt;/td&gt;
&lt;td&gt;Hypervisor Time Drift&lt;/td&gt;
&lt;td&gt;Installed &lt;strong&gt;QEMU Guest Agent&lt;/strong&gt; for hardware sync.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;admin-03&lt;/strong&gt; (Ext. VM)&lt;/td&gt;
&lt;td&gt;MON, OSD.2&lt;/td&gt;
&lt;td&gt;OS-level Time Drift&lt;/td&gt;
&lt;td&gt;Deployed &lt;strong&gt;Chrony&lt;/strong&gt; to force strict NTP sync.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 1: Enforcing Strict NTP on the External VM (&lt;code&gt;admin-03&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Standard &lt;code&gt;systemd-timesyncd&lt;/code&gt; is too sluggish for Ceph. I replaced it with &lt;code&gt;chrony&lt;/code&gt; and forced an immediate clock step on the external Ubuntu VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin-03:~# &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;chrony &lt;span class="nt"&gt;-y&lt;/span&gt;
root@admin-03:~# &lt;span class="nb"&gt;sudo &lt;/span&gt;chronyc makestep
root@admin-03:~# chronyc tracking
Reference ID    : C292FB72 &lt;span class="o"&gt;(&lt;/span&gt;host-194-146-251-114.virtuaoperator.pl&lt;span class="o"&gt;)&lt;/span&gt;
Stratum         : 2
System &lt;span class="nb"&gt;time&lt;/span&gt;     : 0.000000000 seconds slow of NTP &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output confirmed a &lt;strong&gt;0.000000000 seconds&lt;/strong&gt; deviation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Enabling QEMU Guest Agent (&lt;code&gt;admin-02&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;For the nested Proxmox node, time had to be injected directly from the physical hypervisor. I enabled the QEMU Guest Agent flag, performed a hard power cycle of VM &lt;strong&gt;103&lt;/strong&gt;, and verified the communication channel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Executed on the physical host (admin)&lt;/span&gt;
root@admin:~# qm &lt;span class="nb"&gt;set &lt;/span&gt;103 &lt;span class="nt"&gt;-agent&lt;/span&gt; 1
root@admin:~# qm shutdown 103
root@admin:~# qm start 103
root@admin:~# qm agent 103 ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Reconstructing the Corrupted Monitor (&lt;code&gt;admin&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;With the time drift neutralized, I had to fix the broken &lt;code&gt;ceph-mon&lt;/code&gt; daemon on the physical host. Attempting to repair a broken RocksDB is often futile. Since the other two nodes maintained a healthy quorum and an intact Cluster Map, I simply purged the broken node and recreated it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# pveceph mon destroy admin
root@admin:~# pveceph mon create
root@admin:~# systemctl restart ceph-osd@0.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Immediately after the restart, &lt;code&gt;osd.0&lt;/code&gt; successfully authenticated with the fresh Monitor, and the cluster began the peering process, eventually returning to &lt;strong&gt;HEALTH_OK&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Verification: Chaos Engineering
&lt;/h2&gt;

&lt;p&gt;You can't trust a storage cluster until you break it while it's under load. To prove the fix worked, I initiated a continuous benchmark write on the &lt;code&gt;ceph-storage&lt;/code&gt; pool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# rados bench &lt;span class="nt"&gt;-p&lt;/span&gt; ceph-storage 120 write &lt;span class="nt"&gt;--no-cleanup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the benchmark was running at &lt;strong&gt;~55 MB/s&lt;/strong&gt;, I executed a brutal &lt;code&gt;sudo systemctl poweroff&lt;/code&gt; on the external &lt;strong&gt;admin-03&lt;/strong&gt; node.&lt;/p&gt;

&lt;p&gt;Here is what happened in the benchmark logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  38       16       419       403   42.4168         0            -     1.12533
  39       16       419       403   41.3292         0            -     1.12533
2026-03-25T22:31:25.378550+0100 min lat: 0.334379 max lat: 3.14551 avg lat: 1.12533
  40       16       419       403   40.2959         0            -     1.12533
  41       16       419       403   39.3131         0            -     1.12533
  42       16       439       423   40.2816         8     0.399663      1.5659
  43       16       470       454   42.2282       124     0.593991      1.5049
  44       16       489       473   42.9956        76     0.755278     1.47177
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Seconds 38-41:&lt;/strong&gt; The moment the node died, the cluster momentarily paused client I/O. Throughput dropped to &lt;strong&gt;0 MB/s&lt;/strong&gt;, and latency spiked.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Second 42+:&lt;/strong&gt; The CRUSH map recalculated the topology. Because the pool was configured with &lt;strong&gt;min_size = 2&lt;/strong&gt;, the two surviving nodes authorized the writes. The cluster automatically resumed operations, quickly catching up and hitting speeds of &lt;strong&gt;124 MB/s&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the test concluded, I cleaned up the benchmark data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# rados &lt;span class="nt"&gt;-p&lt;/span&gt; ceph-storage cleanup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Time synchronization is the absolute lifeblood of Ceph. Never trust default virtual machine clocks in a production or simulated lab environment. By implementing &lt;code&gt;chrony&lt;/code&gt; and QEMU hardware sync, the cluster is now fully immune to node-level failures.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ceph</category>
      <category>proxmox</category>
    </item>
    <item>
      <title>Proxmox &amp; Ceph in a Home Lab: Building a Cluster from a Single Drive &amp; Surviving the CLI Battle</title>
      <dc:creator>Konrad Kądzielawa</dc:creator>
      <pubDate>Mon, 23 Mar 2026 19:58:21 +0000</pubDate>
      <link>https://dev.to/kkadzielawa/proxmox-ceph-in-a-home-lab-building-a-cluster-from-a-single-drive-surviving-the-cli-battle-4f6a</link>
      <guid>https://dev.to/kkadzielawa/proxmox-ceph-in-a-home-lab-building-a-cluster-from-a-single-drive-surviving-the-cli-battle-4f6a</guid>
      <description>&lt;p&gt;Building a &lt;a href="https://pve.proxmox.com/pve-docs/chapter-pveceph.html" rel="noopener noreferrer"&gt;Ceph cluster on Proxmox VE&lt;/a&gt; in a home lab is a fantastic proving ground. The catch is that Ceph is inherently a powerful Enterprise-grade system – and by default, it demands &lt;a href="https://docs.ceph.com/en/latest/start/hardware-recommendations/" rel="noopener noreferrer"&gt;entire, raw physical drives (raw block devices)&lt;/a&gt; for its OSD (Object Storage Daemon) daemons.&lt;/p&gt;

&lt;p&gt;But what if your "IT Bunker" only has one physical server with a single extra 111 GB SSD, and you want to simulate a full-fledged 3-node cluster? My architecture looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;admin&lt;/strong&gt; (Physical Proxmox host)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;admin-02&lt;/strong&gt; (Nested VM on the main server)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;admin-03&lt;/strong&gt; (VM... running remotely on a separate desktop PC)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the story of how I "cheated" Ceph's strict mechanics, sliced a single drive into pieces, bypassed hypervisor GUI errors, and ultimately achieved the coveted &lt;code&gt;HEALTH_OK&lt;/code&gt; status.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 1: Handing an LVM Partition over to Ceph
&lt;/h2&gt;

&lt;p&gt;My plan was to split the physical &lt;code&gt;sdc&lt;/code&gt; (111.8 GB) drive on the main server into three 35 GB logical volumes using &lt;a href="https://wiki.debian.org/LVM" rel="noopener noreferrer"&gt;LVM (Logical Volume Manager)&lt;/a&gt;. Here is the &lt;code&gt;lsblk&lt;/code&gt; output from the main host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sdc                            111.8G LVM2_member disk 
├─vg_ceph_lab-ceph_admin          35G             lvm  
└─vg_ceph_lab-ceph_admin02        35G             lvm  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first node (&lt;code&gt;admin&lt;/code&gt;) took its piece without a hitch. The trouble started when I tried to add an OSD for the second node (&lt;code&gt;admin-02&lt;/code&gt;) via the main host's CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# pveceph osd create /dev/vg_ceph_lab/ceph_admin
unable to get device info &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;'/dev/dm-6'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Proxmox and Ceph hate shared LVM paths and standard partitions when creating an OSD. Since &lt;code&gt;admin-02&lt;/code&gt; is a virtual machine, I leveraged the built-in &lt;a href="https://www.google.com/search?q=%5Bhttps://pve.proxmox.com/pve-docs/qm.1.html%5D(https://pve.proxmox.com/pve-docs/qm.1.html)" rel="noopener noreferrer"&gt;QEMU/KVM Virtual Machine Manager (&lt;code&gt;qm&lt;/code&gt;) tool&lt;/a&gt;. Instead of fighting Ceph daemons on the host, I attached the virtual volume directly into the &lt;code&gt;admin-02&lt;/code&gt; VM, bypassing the filesystem layer entirely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm &lt;span class="nb"&gt;set &lt;/span&gt;103 &lt;span class="nt"&gt;-scsi2&lt;/span&gt; /dev/vg_ceph_lab/ceph_admin02
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;admin-02&lt;/code&gt; VM, the OS saw new, "physical", and raw block hardware. A quick click on &lt;em&gt;Create OSD&lt;/em&gt; in the node's Proxmox GUI, and the drive initialized perfectly!&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 2: An External Host and a Rebellious virt-manager
&lt;/h2&gt;

&lt;p&gt;That left the third node: &lt;code&gt;admin-03&lt;/code&gt;. The problem? This VM physically resided on my Linux Desktop PC across the room. The &lt;code&gt;qm set&lt;/code&gt; command was useless here because the sliced LVM volume was on the server in the rack.&lt;/p&gt;

&lt;p&gt;In a cluster architecture, Ceph doesn't care where the physical storage comes from. I decided to ditch the server's LVM for this node and create a new virtual disk locally on my PC. But trying to add a disk via the &lt;a href="https://virt-manager.org/" rel="noopener noreferrer"&gt;virt-manager GUI&lt;/a&gt; threw a hardware incompatibility error: &lt;code&gt;pc-q35-noble&lt;/code&gt;. The graphical interface completely refused to cooperate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; When the GUI throws weird exceptions, the terminal is your best friend. I fell back on native tools from the &lt;a href="https://libvirt.org/docs.html" rel="noopener noreferrer"&gt;libvirt&lt;/a&gt; and QEMU stack.&lt;/p&gt;

&lt;p&gt;First, I manually generated the disk file using &lt;a href="https://www.google.com/search?q=%5Bhttps://qemu-project.gitlab.io/qemu/tools/qemu-img.html%5D(https://qemu-project.gitlab.io/qemu/tools/qemu-img.html)" rel="noopener noreferrer"&gt;&lt;code&gt;qemu-img&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;qemu-img create &lt;span class="nt"&gt;-f&lt;/span&gt; qcow2 /var/lib/libvirt/images/admin-03-ceph.qcow2 35G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I hard-attached the generated image to the offline VM configuration using &lt;code&gt;virsh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh attach-disk Proxmox-Lab /var/lib/libvirt/images/admin-03-ceph.qcow2 vdb &lt;span class="nt"&gt;--config&lt;/span&gt; &lt;span class="nt"&gt;--subdriver&lt;/span&gt; qcow2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon booting &lt;code&gt;admin-03&lt;/code&gt;, its internal Proxmox system recognized the &lt;code&gt;/dev/vdb&lt;/code&gt; drive. Adding the third OSD in the Proxmox GUI worked flawlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finale: Pools, Replication, and Green Status
&lt;/h2&gt;

&lt;p&gt;After adding the third OSD, I had a "Frankenstein" cluster: drives supplied by raw LVM from a physical server mixed with a remote &lt;code&gt;qcow2&lt;/code&gt; virtual disk. Did Ceph care? Not at all.&lt;/p&gt;

&lt;p&gt;The final step was to create a &lt;a href="https://docs.ceph.com/en/latest/rados/operations/pools/" rel="noopener noreferrer"&gt;Ceph Pool&lt;/a&gt;—the logical organizational layer where the actual data lands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Size: 3&lt;/strong&gt; (Every data block is replicated in 3 copies).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Min. Size: 2&lt;/strong&gt; (The cluster allows I/O writes as long as at least 2 out of 3 nodes are active).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CLI verification (on the main node) showed exactly what I had been fighting for all evening:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# ceph &lt;span class="nt"&gt;-s&lt;/span&gt;
  cluster:
    &lt;span class="nb"&gt;id&lt;/span&gt;:     25f2d0a2-d64b-42a2-b93d-99a560571e0e
    health: HEALTH_OK

  services:
    mon: 3 daemons, quorum admin,admin-02,admin-03 &lt;span class="o"&gt;(&lt;/span&gt;age 6m&lt;span class="o"&gt;)&lt;/span&gt;
    mgr: admin&lt;span class="o"&gt;(&lt;/span&gt;active, since 60m&lt;span class="o"&gt;)&lt;/span&gt;, standbys: admin-02, admin-03
    osd: 3 osds: 3 up &lt;span class="o"&gt;(&lt;/span&gt;since 6m&lt;span class="o"&gt;)&lt;/span&gt;, 3 &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;since 6m&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check the &lt;a href="https://docs.ceph.com/en/latest/rados/operations/crush-map/" rel="noopener noreferrer"&gt;CRUSH (Controlled Replication Under Scalable Hashing)&lt;/a&gt; algorithm tree using the &lt;code&gt;ceph osd tree&lt;/code&gt; command to confirm the cluster topology:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@admin:~# ceph osd tree
ID  CLASS WEIGHT   TYPE NAME         STATUS  REWEIGHT  PRI-AFF
&lt;span class="nt"&gt;-1&lt;/span&gt;         0.10258 root default                      
&lt;span class="nt"&gt;-3&lt;/span&gt;         0.03419     host admin                     
 0   hdd  0.03419         osd.0          up   1.00000  1.00000
&lt;span class="nt"&gt;-5&lt;/span&gt;         0.03419     host admin-02                  
 1   hdd  0.03419         osd.1          up   1.00000  1.00000
&lt;span class="nt"&gt;-7&lt;/span&gt;         0.03419     host admin-03                  
 2   hdd  0.03419         osd.2          up   1.00000  1.00000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, Ceph's intelligent engine sees my crafted nodes as separate physical &lt;code&gt;hosts&lt;/code&gt; and correctly distributes the traffic, providing me with a single, unified &lt;code&gt;rbd&lt;/code&gt; (RADOS Block Device) storage pool labeled &lt;code&gt;ceph-storage&lt;/code&gt; in Proxmox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Home lab experiments quickly expose the gap between dry documentation theory and actual implementations in simulated environments. This deployment taught me two key lessons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Understanding how to pass storage resources through the hypervisor (like the &lt;code&gt;qm set&lt;/code&gt; tool in PVE) is a game changer when Ceph insists on block access and you only have LVM partitions available.&lt;/li&gt;
&lt;li&gt; When GUI software (like desktop &lt;code&gt;virt-manager&lt;/code&gt; in my case) throws motherboard configuration errors, going back to the basics and pure CLI (&lt;code&gt;virsh&lt;/code&gt;, &lt;code&gt;qemu-img&lt;/code&gt;) lets you bypass those issues in just a few quick commands.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With a fully functioning, green cluster, I am ready for the next level: Chaos Engineering. I plan to disconnect drives while VMs are running just to see if my Ceph setup is truly as resilient as the documentation promises!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ceph</category>
      <category>proxmox</category>
    </item>
    <item>
      <title>DNS Troubleshooting on Windows: When the Internet Works but the Intranet Doesn’t</title>
      <dc:creator>Konrad Kądzielawa</dc:creator>
      <pubDate>Fri, 20 Mar 2026 12:10:01 +0000</pubDate>
      <link>https://dev.to/kkadzielawa/dns-troubleshooting-on-windows-when-the-internet-works-but-the-intranet-doesnt-1459</link>
      <guid>https://dev.to/kkadzielawa/dns-troubleshooting-on-windows-when-the-internet-works-but-the-intranet-doesnt-1459</guid>
      <description>&lt;p&gt;One of the more frustrating problems in IT is when a user says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The internet works, but this one internal site doesn’t.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first, it sounds simple. Public websites load fine. Email works. Teams works. But the internal app still refuses to open, or it loads painfully slowly.&lt;/p&gt;

&lt;p&gt;In situations like that, the application itself is not always the real problem. Quite often, the issue is one layer lower: DNS.&lt;/p&gt;

&lt;p&gt;That is also why I think DNS is worth understanding in a practical way, not just as a protocol you read about once and then forget. In day-to-day admin work, name resolution problems show up more often than people expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  A very typical scenario
&lt;/h2&gt;

&lt;p&gt;Let’s take a neutral example.&lt;/p&gt;

&lt;p&gt;A user tries to open:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://intranet-app01/portal&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What they see is something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the page does not open at all, or takes too long to load,&lt;/li&gt;
&lt;li&gt;public websites still work,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nslookup intranet-app01&lt;/code&gt; returns an IP address,&lt;/li&gt;
&lt;li&gt;but the internal application still behaves as if something is wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, it is very tempting to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If &lt;code&gt;nslookup&lt;/code&gt; works, DNS is fine.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used to think that too in simpler cases. But in real environments, that conclusion is often too optimistic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;nslookup&lt;/code&gt; helps — but does not always tell the full story
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/nslookup" rel="noopener noreferrer"&gt;&lt;code&gt;nslookup&lt;/code&gt;&lt;/a&gt; is still a useful tool. It is quick, simple, and good for checking what a DNS server returns for a given name.&lt;/p&gt;

&lt;p&gt;The catch is that a successful &lt;code&gt;nslookup&lt;/code&gt; result does &lt;strong&gt;not always mean the client is resolving names cleanly in real use&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Microsoft’s own &lt;a href="https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/troubleshoot-dns-client-resolution-issues" rel="noopener noreferrer"&gt;DNS client troubleshooting guide&lt;/a&gt; shows why this matters. If a machine has multiple DNS servers configured and some of them are unreachable, Windows may still eventually resolve the name — but only after retries and timeouts. From the user’s point of view, that can look like “the app is slow” or “the site is broken,” even though DNS eventually returns an answer.&lt;/p&gt;

&lt;p&gt;So when I troubleshoot DNS now, I try not to stop at:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Does the name resolve?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How does it resolve, from where, and how long does it take?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How Windows actually resolves a name
&lt;/h2&gt;

&lt;p&gt;One detail I find especially useful from Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/troubleshoot-dns-client-resolution-issues" rel="noopener noreferrer"&gt;troubleshooting article&lt;/a&gt; is that Windows does not immediately send every query to a DNS server.&lt;/p&gt;

&lt;p&gt;The client typically goes through these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;check the local DNS cache&lt;/li&gt;
&lt;li&gt;check the &lt;code&gt;Hosts&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;query the DNS server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That means a “correct” answer is not always coming from the place you think it is.&lt;/p&gt;

&lt;p&gt;Sometimes the client is using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an old cached entry,&lt;/li&gt;
&lt;li&gt;a negative cache result,&lt;/li&gt;
&lt;li&gt;or a static entry in the &lt;code&gt;Hosts&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So even when name resolution appears to work, it is still worth asking whether you are looking at live DNS behavior or just local state.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple workflow I usually follow
&lt;/h2&gt;

&lt;p&gt;When an internal hostname behaves strangely, I try not to jump between random fixes. I usually go through a short set of checks in the same order.&lt;/p&gt;

&lt;p&gt;It does not solve every case, but it helps narrow things down quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Check the client’s DNS configuration
&lt;/h3&gt;

&lt;p&gt;I usually start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ipconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/all&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives me a quick view of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which DNS servers are configured,&lt;/li&gt;
&lt;li&gt;in what order,&lt;/li&gt;
&lt;li&gt;and whether anything looks off right away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I see multiple DNS servers, I want to know whether all of them are still reachable. Microsoft notes in its &lt;a href="https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/troubleshoot-dns-client-resolution-issues" rel="noopener noreferrer"&gt;DNS troubleshooting guide&lt;/a&gt; that unreachable DNS servers can introduce noticeable delays because the client keeps trying before moving on.&lt;/p&gt;

&lt;p&gt;That is one of those things that can make an application feel broken even though the underlying name eventually resolves.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Compare the default result with a specific DNS server
&lt;/h3&gt;

&lt;p&gt;Next, I usually run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;nslookup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;intranet-app01&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;nslookup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;intranet-app01.corp.example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;192.0.2.53&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command tells me what happens with the machine’s default DNS configuration.&lt;/p&gt;

&lt;p&gt;The second helps remove some ambiguity, because I am querying a specific DNS server directly.&lt;/p&gt;

&lt;p&gt;According to the official &lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/nslookup" rel="noopener noreferrer"&gt;&lt;code&gt;nslookup&lt;/code&gt; documentation&lt;/a&gt;, you can provide the DNS server explicitly in non-interactive mode, which makes this a useful comparison step.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Test the same name with &lt;code&gt;Resolve-DnsName&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;After that, I often repeat the test in PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Resolve-DnsName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;intranet-app01.corp.example&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Resolve-DnsName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;intranet-app01.corp.example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;192.0.2.53&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/powershell/module/dnsclient/resolve-dnsname?view=windowsserver2025-ps" rel="noopener noreferrer"&gt;&lt;code&gt;Resolve-DnsName&lt;/code&gt; documentation&lt;/a&gt; describes it as functionally similar to &lt;code&gt;nslookup&lt;/code&gt;, and I find it really useful when I want clearer, more structured output.&lt;/p&gt;

&lt;p&gt;It is especially helpful when I want to compare answers from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the default client path,&lt;/li&gt;
&lt;li&gt;and a specific DNS server I actually trust for that zone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This does not automatically fix anything, of course, but it helps answer a more precise question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Is the issue really DNS, or is it the path the client is taking to get the answer?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Inspect the local DNS cache
&lt;/h3&gt;

&lt;p&gt;If the results still feel inconsistent, I check the local resolver cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ipconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/displaydns&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ipconfig" rel="noopener noreferrer"&gt;&lt;code&gt;ipconfig&lt;/code&gt; reference&lt;/a&gt; explains that &lt;code&gt;/displaydns&lt;/code&gt; shows the DNS resolver cache, including entries loaded from the local &lt;code&gt;Hosts&lt;/code&gt; file and recently resolved records.&lt;/p&gt;

&lt;p&gt;I like this step because it helps explain a lot of confusing cases.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maybe the client is not querying DNS at all anymore,&lt;/li&gt;
&lt;li&gt;maybe it is holding on to an old result,&lt;/li&gt;
&lt;li&gt;maybe there is a local entry that makes the test misleading.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Flush the cache if I suspect stale local state
&lt;/h3&gt;

&lt;p&gt;If I think the cache might be part of the problem, I clear it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ipconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/flushdns&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, this is documented by Microsoft in the &lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ipconfig" rel="noopener noreferrer"&gt;&lt;code&gt;ipconfig&lt;/code&gt; command reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I would not treat this as a magic fix, because it is not. But it is a reasonable troubleshooting step when I want to remove stale local state before testing again.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Check whether the short name is the real issue
&lt;/h3&gt;

&lt;p&gt;This is probably one of the most common things behind “it works for some people but not for others.”&lt;/p&gt;

&lt;p&gt;Users often type:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://intranet-app01&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;instead of:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://intranet-app01.corp.example&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That difference matters more than it seems.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/troubleshoot-dns-client-resolution-issues" rel="noopener noreferrer"&gt;DNS troubleshooting guide&lt;/a&gt; includes examples showing how the DNS suffix search list can affect resolution time and behavior. If the suffix list is long, badly ordered, or just not aligned with the environment, short-name resolution can become slow or inconsistent.&lt;/p&gt;

&lt;p&gt;So sometimes the problem is not that DNS is “down.”&lt;/p&gt;

&lt;p&gt;Sometimes the problem is simply that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the short name is ambiguous,&lt;/li&gt;
&lt;li&gt;the client is trying the wrong suffixes first,&lt;/li&gt;
&lt;li&gt;or the right answer only shows up after too many failed attempts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What usually turns out to be wrong
&lt;/h2&gt;

&lt;p&gt;In many environments, DNS is not completely broken.&lt;/p&gt;

&lt;p&gt;It is just &lt;strong&gt;slow, inconsistent, or slightly misleading&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice, I have found that the most common causes are things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the primary DNS server is unreachable,&lt;/li&gt;
&lt;li&gt;DNS servers are configured in a bad order,&lt;/li&gt;
&lt;li&gt;the client is using a cached result,&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;Hosts&lt;/code&gt; file contains an override,&lt;/li&gt;
&lt;li&gt;users rely on short names instead of FQDNs,&lt;/li&gt;
&lt;li&gt;the DNS suffix search list is too long or poorly ordered,&lt;/li&gt;
&lt;li&gt;or the application is simply less tolerant of delays than a browser is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why I try not to stop at “&lt;code&gt;nslookup&lt;/code&gt; works.”&lt;/p&gt;

&lt;p&gt;That statement may be true and still not explain the user’s experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question I actually want to answer
&lt;/h2&gt;

&lt;p&gt;When I troubleshoot something like this, I am not really asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Does the name resolve?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am usually asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How is the client resolving it, what is it talking to first, and where is the delay happening?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small shift in thinking helps a lot.&lt;/p&gt;

&lt;p&gt;It turns troubleshooting from random guessing into a more predictable workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick checklist I like to keep in mind
&lt;/h2&gt;

&lt;p&gt;Here is the short version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. ipconfig /all
2. nslookup shortname
3. nslookup FQDN &amp;lt;specific_DNS_server&amp;gt;
4. Resolve-DnsName -Name FQDN
5. Resolve-DnsName -Name FQDN -Server &amp;lt;specific_DNS_server&amp;gt;
6. ipconfig /displaydns
7. ipconfig /flushdns
8. test the FQDN instead of the short name
9. review DNS server order and suffix search list
10. only then move deeper into the application itself
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;A lot of DNS issues do not look like DNS issues at first.&lt;/p&gt;

&lt;p&gt;They look like browser problems, application issues, random network instability, or “something weird on this one computer.”&lt;/p&gt;

&lt;p&gt;But sometimes the underlying cause is much simpler than that:&lt;/p&gt;

&lt;p&gt;the client is asking the wrong place, in the wrong order, for too long.&lt;/p&gt;

&lt;p&gt;That is why I try to troubleshoot DNS methodically instead of jumping straight into random fixes. It saves time, and it usually makes the actual problem easier to explain — both to yourself and to the user who just wants the internal site to work again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;If you want to dig a bit deeper, these official Microsoft references are worth bookmarking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/nslookup" rel="noopener noreferrer"&gt;&lt;code&gt;nslookup&lt;/code&gt; command reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/powershell/module/dnsclient/resolve-dnsname?view=windowsserver2025-ps" rel="noopener noreferrer"&gt;&lt;code&gt;Resolve-DnsName&lt;/code&gt; PowerShell documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ipconfig" rel="noopener noreferrer"&gt;&lt;code&gt;ipconfig&lt;/code&gt; command reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/troubleshoot-dns-client-resolution-issues" rel="noopener noreferrer"&gt;Troubleshoot DNS client name resolution issues&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>dns</category>
      <category>microsoft</category>
      <category>networking</category>
    </item>
    <item>
      <title>Building a Zero-Maintenance SysOps Knowledge Base with React and the Dev.to API</title>
      <dc:creator>Konrad Kądzielawa</dc:creator>
      <pubDate>Wed, 18 Feb 2026 12:25:24 +0000</pubDate>
      <link>https://dev.to/kkadzielawa/building-a-zero-maintenance-sysops-knowledge-base-with-react-and-the-devto-api-36lp</link>
      <guid>https://dev.to/kkadzielawa/building-a-zero-maintenance-sysops-knowledge-base-with-react-and-the-devto-api-36lp</guid>
      <description>&lt;p&gt;After spending nearly a decade navigating different areas of IT—from building complex e-commerce frontends to managing the underlying Linux servers and infrastructure—I decided it was time to completely rebuild my personal portfolio.&lt;/p&gt;

&lt;p&gt;My old website was full of typical "frontend fireworks": heavy animations, flashy graphics, and complex layouts. But as my career naturally shifted towards IT Systems Administration and infrastructure management, that style no longer resonated with my daily work. I wanted a space that felt like home to a SysOps engineer: raw, minimalist, and terminal-inspired.&lt;/p&gt;

&lt;p&gt;However, as an admin, I had a strict set of requirements for this new platform:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero maintenance:&lt;/strong&gt; I don't want to deal with database backups or traditional CMS panels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No local Markdown files:&lt;/strong&gt; Pushing a commit to GitHub just to fix a typo in a blog post is an overkill that breaks the writing flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Source of Truth:&lt;/strong&gt; I want to write technical notes once and have them automatically distribute to my community profile and my personal website.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The solution? &lt;strong&gt;Using the Dev.to API as a free, headless CMS for a React Single Page Application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is a technical breakdown of how I designed this architecture&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Fetching the Data (The API Layer)
&lt;/h2&gt;

&lt;p&gt;Dev.to provides a fantastic, mostly undocumented perk for developers: a public, read-only API that returns your articles in JSON format. &lt;/p&gt;

&lt;p&gt;Instead of hardcoding data, my React app fetches the article list directly from my Dev.to profile upon initialization. To prevent unnecessary network requests and ensure a 0ms load time when navigating back and forth, I implemented a simple in-memory cache outside the component lifecycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Global cache for instant UI rendering&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cachedArticles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DevToArticle&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MainAdmin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setArticles&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DevToArticle&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedArticles&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedArticles&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;cachedArticles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[https://dev.to/api/articles?username=kkadzielawa](https://dev.to/api/articles?username=kkadzielawa)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setArticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;cachedArticles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Cache the response&lt;/span&gt;
          &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="c1"&gt;// ... render logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Dynamic Tag Filtering
&lt;/h2&gt;

&lt;p&gt;Since the Dev.to API returns an array of tags (&lt;code&gt;tag_list&lt;/code&gt;) for each article, I didn't need to build a custom taxonomy system. I simply extract all unique tags dynamically from the fetched payload and generate a terminal-like filter menu &lt;code&gt;[ #linux ]&lt;/code&gt;, &lt;code&gt;[ #docker ]&lt;/code&gt;, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The "Optimistic UI" Trick
&lt;/h2&gt;

&lt;p&gt;One downside of the &lt;code&gt;/articles&lt;/code&gt; list endpoint is that it doesn't return the full Markdown body of the posts. To get the content, you have to make a second request to &lt;code&gt;/articles/{id}&lt;/code&gt; when the user clicks on a specific note.&lt;/p&gt;

&lt;p&gt;To prevent the annoying "layout shift" and loading spinners, I used React Router's &lt;code&gt;state&lt;/code&gt; to pass the title of the article from the main list directly to the reader component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; 
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/note/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Pass the title forward!&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to this, the note viewer immediately renders the title and structure, while the actual Markdown body stream loads discreetly in the background.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Rendering Markdown with Syntax Highlighting
&lt;/h2&gt;

&lt;p&gt;Once the API returns the &lt;code&gt;body_markdown&lt;/code&gt;, it needs to be safely parsed into HTML. Since this is a SysOps blog, code snippets must look flawless. I combined &lt;code&gt;react-markdown&lt;/code&gt; with &lt;code&gt;react-syntax-highlighter&lt;/code&gt; using the VS Code Dark Plus theme for a native IDE feel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactMarkdown&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-markdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Prism&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;SyntaxHighlighter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-syntax-highlighter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vscDarkPlus&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-syntax-highlighter/dist/esm/styles/prism&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Inside the render function:&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ReactMarkdown&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/language-&lt;/span&gt;&lt;span class="se"&gt;(\w&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SyntaxHighlighter&lt;/span&gt;
          &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vscDarkPlus&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;PreTag&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"div"&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SyntaxHighlighter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"inline-code"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ReactMarkdown&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;By treating Dev.to as a completely touchless, headless CMS, I achieved a perfect &lt;strong&gt;Separation of Concerns&lt;/strong&gt;. Dev.to handles the database, image hosting, SEO, and the writing experience. My React application acts purely as a customized, terminal-themed presentation layer.&lt;/p&gt;

&lt;p&gt;My personal portfolio updates automatically. That's the exact kind of automation every IT Admin loves.&lt;/p&gt;

&lt;p&gt;Now, whenever I want to drop some technical meat (like a new Linux configuration or securing a server environment), I just hit "Publish" on Dev.to, and it's instantly live.&lt;/p&gt;

&lt;p&gt;Check out the live result at &lt;a href="https://kadzielawa.dev" rel="noopener noreferrer"&gt;kadzielawa.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Server-Side Rendering (SSR) vs. Client-Side Rendering (CSR): The Fascinating World of Page Rendering</title>
      <dc:creator>Konrad Kądzielawa</dc:creator>
      <pubDate>Fri, 01 Dec 2023 15:28:31 +0000</pubDate>
      <link>https://dev.to/kkadzielawa/server-side-rendering-ssr-vs-client-side-rendering-csr-the-fascinating-world-of-page-rendering-2fba</link>
      <guid>https://dev.to/kkadzielawa/server-side-rendering-ssr-vs-client-side-rendering-csr-the-fascinating-world-of-page-rendering-2fba</guid>
      <description>&lt;p&gt;The choice between Server-Side Rendering (SSR) and Client-Side Rendering (CSR) is not just a technological decision. It's like choosing between classic art and modern cinema - each has its unique characteristics and hidden treasures to discover.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Side Rendering (SSR): Classical Elegance
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Advantages of SSR:&lt;/strong&gt;&lt;br&gt;
SEO Magic: With SSR, your website is like a well-written book, easy to find and read by search engines.&lt;br&gt;
Fast First Impression: SSR delivers content quickly, like an express courier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages of SSR:&lt;/strong&gt;&lt;br&gt;
Heavier Server Load: SSR is a bit like working full-time for the server - it requires continuous attention and resources.&lt;/p&gt;

&lt;p&gt;SSR is like visiting an elegant, classical art gallery, where each artwork is carefully prepared and presented with attention to detail. In SSR, every website is like a finely crafted painting, ready to be admired immediately upon entry. This approach combines solidity and reliability, offering users and search engines complete, ready-to-consume content. As a result, SSR pages are like well-tailored suits: stylish, professional, and always in good taste, perfectly suited for the world of SEO and making a fast first impression.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.google.com/search?q=Server-side+rendering" rel="noopener noreferrer"&gt;Discover the secrets of SSR.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side Rendering (CSR): Modern Theater of Interaction
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Advantages of CSR:&lt;/strong&gt;&lt;br&gt;
Mastery of Interaction: CSR is like a director who adapts the scene to the audience's mood - dynamic and full of surprises.&lt;br&gt;
Server Resource Savings: It's like remote work for the server - fewer loads, more flexibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages of CSR:&lt;/strong&gt;&lt;br&gt;
SEO Puzzle: For search engines, CSR pages can sometimes be like a complicated puzzle.&lt;br&gt;
CSR is nothing but entering a modern theater of interaction, where each scene is created live, in response to the viewer's actions. CSR is like a dynamic, improvised performance in which the website's content unfolds and evolves in real-time in the user's browser. It's a world where interactivity and adaptability play the lead roles, and the website is constantly in motion, like a crowd in a bustling city center. CSR brings what is most modern and exciting in web development – a site that not only responds but dances to the user's rhythm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.google.com/search?q=client+side+rendering" rel="noopener noreferrer"&gt;Immerse yourself in the world of CSR.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Blending Dimensions: Hybrid Rendering Techniques
&lt;/h2&gt;

&lt;p&gt;Using hybrid rendering methods is like the art of culinary fusion, where the chef combines traditional ingredients with modern cooking techniques to create surprising and complex dishes. In the world of web development, hybrid approaches blend the solidity and reliability of SSR with the flexibility and dynamism of CSR, creating unique "dishes" in the digital space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Innovation in technology:&lt;/strong&gt; Tools like Next.js for React are redefining the frameworks in which developers create web experiences. By combining the fast loading and SEO optimization offered by SSR with the reactivity and rich interactions of CSR, hybrid approaches create platforms that are both efficient and engaging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interesting IT fact:&lt;/strong&gt; Using hybrid approaches in web design is like balancing between two worlds. Developers can decide which elements of the website are best rendered on the server-side and which on the client-side, similar to how a chef decides when to combine classic techniques with modern ones for the best effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Hybrid rendering techniques open up new possibilities in web development, allowing for the creation of websites that combine the best features of both worlds. Just as in culinary art, where the combination of old and new techniques creates amazing dishes, in web design, combining SSR and CSR creates unforgettable digital experiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
The choice between SSR and CSR is not just a technical decision. It's a choice between two different ways of storytelling on the internet - one old and proven, and the other new and exciting. Understanding their unique features and benefits allows for the creation of better, more engaging websites.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Programming in 2033 – Vision of the Future of Software Development</title>
      <dc:creator>Konrad Kądzielawa</dc:creator>
      <pubDate>Fri, 01 Dec 2023 15:20:47 +0000</pubDate>
      <link>https://dev.to/kkadzielawa/programming-in-2033-vision-of-the-future-of-software-development-2bbb</link>
      <guid>https://dev.to/kkadzielawa/programming-in-2033-vision-of-the-future-of-software-development-2bbb</guid>
      <description>&lt;p&gt;In the last decade, the world of technology has undergone a metamorphosis, and software development has become a part of an exciting future. In 2033, the work of programmers is full of extraordinary experiences, seeming like something out of fantasy from years past, which we could only dream of not too long ago. Let's contemplate what their reality looks like... let's delve into this future, discovering the latest innovations and futuristic trends in technology.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code-Generating Algorithms:&lt;/strong&gt;&lt;br&gt;
In the year 2033, programmers have become supervisors and process engineers, having long delegated some tasks to advanced code-generating algorithms. Thanks to advanced tools, programmers exclusively use a thought interface to communicate with artificial intelligence systems that create and optimize code according to their intentions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#Example: A programmer thinks about a function to add two numbers.&lt;br&gt;
Text = "Create a function that adds two numbers."&lt;br&gt;
AI.create_code(text)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.blog/2023-04-14-how-generative-ai-is-changing-the-way-developers-work/" rel="noopener noreferrer"&gt;Discover how AI is changing programming.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The World of Cybersecurity:&lt;/strong&gt;&lt;br&gt;
In an era full of advanced hackers and AI, the importance of cybersecurity has reached its zenith. Programmers are now cyber-wizards, using tools based on neural algorithms to defend our digital worlds against constantly evolving threats.&lt;/p&gt;

&lt;p&gt;Cybersecurity in 2033 is not just about fighting threats; it's also about the art of predicting potential attacks through predictive analysis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cybriant.com/top-cyber-security-websites-of-2022/" rel="noopener noreferrer"&gt;Explore the latest trends in cybersecurity.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Ubiquity of Clouds and Quantum Computing:&lt;/strong&gt;&lt;br&gt;
A key element of a programmer's work in 2033 is cloud computing and solutions based on quantum computing. Programmers use such computers to solve problems that were once thought impossible to solve in a traditional way. Serverless and microservices solutions have become the norm, requiring programmers to have the skills to manage complex, distributed systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bluexp.netapp.com/blog/cvo-blg-the-future-of-cloud-computing-5-trends-you-must-know-about" rel="noopener noreferrer"&gt;Get to know the future of the cloud.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual and Augmented Reality in Everyday Work:&lt;/strong&gt;&lt;br&gt;
Programmers in 2033 use VR and AR technologies not only for design but also as their daily work tools. Using thought interfaces, they design three-dimensional software models that can be "touched" and edited in virtual space.&lt;/p&gt;

&lt;p&gt;In the world of VR, programmers work within their projects, moving through virtual spaces to solve problems and test applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.toptal.com/designers/ui/vr-ar-design-guide" rel="noopener noreferrer"&gt;Immerse yourself in the world of VR/AR for programmers.&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;The Blossoming of Soft Skills:&lt;/strong&gt;&lt;br&gt;
In an era where machines handle technical details, soft skills have become a key asset for programmers. Communication, creativity, and the ability to work in teams are essential for developing innovative solutions.&lt;/p&gt;

&lt;p&gt;Programmers participate in simulations of customer interactions to refine their soft skills and understand user needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.comptia.org/career-change/exploring-it/skills-for-it" rel="noopener noreferrer"&gt;Learn more about soft skills in IT.&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Never-ending Education:&lt;/strong&gt;&lt;br&gt;
In 2033, learning knows no bounds. Programmers are eternal students, using interactive learning interfaces that enable them to quickly grasp the latest technologies and concepts.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#Example: An interactive AI-based learning interface.&lt;br&gt;
AI.learn("New_Technology")&lt;br&gt;
AI.connect_with_mentor("Name Surname")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://teambuilding.com/blog/online-learning-platforms" rel="noopener noreferrer"&gt;Check out the best educational platforms for programmers.&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;br&gt;
The work of programmers in 2033 is an exciting journey into the future, full of interactions with intelligent algorithms, quantum computing, virtual worlds, and continuous learning. It's a world where humans and machines collaborate to create innovative solutions that transform the face of technological reality. Artificial intelligence, cloud solutions, VR/AR, and cybersecurity play crucial roles. This evolution of the profession demonstrates how adaptability and the ability to learn are essential in a dynamically changing world of technology.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
