<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Traefik on UmiLife's Blog</title><link>https://umi4.life/tags/traefik/</link><description>Recent content from UmiLife's Blog</description><generator>Hugo</generator><language>en-us</language><managingEditor>zekamashi@umi4.life (Umi4Life)</managingEditor><webMaster>zekamashi@umi4.life (Umi4Life)</webMaster><copyright>All articles on this blog are licensed under the BY-NC-SA license agreement unless otherwise stated. Please indicate the source when reprinting!</copyright><lastBuildDate>Wed, 03 Jun 2026 18:30:00 +0700</lastBuildDate><atom:link href="https://umi4.life/tags/traefik/index.xml" rel="self" type="application/rss+xml"/><item><title>Untangling the Homelab After a Hermes Dashboard Setup Went Sideways</title><link>https://umi4.life/posts/hermes-litellm-authelia-control-plane/</link><pubDate>Wed, 03 Jun 2026 18:30:00 +0700</pubDate><author>zekamashi@umi4.life (Umi4Life)</author><guid>https://umi4.life/posts/hermes-litellm-authelia-control-plane/</guid><description>
<![CDATA[<h1>Untangling the Homelab After a Hermes Dashboard Setup Went Sideways</h1><p>Author: Umi4Life(zekamashi@umi4.life)</p>
        
          <h1 id="untangling-the-homelab-after-a-hermes-dashboard-setup-went-sideways">
<a class="header-anchor" href="#untangling-the-homelab-after-a-hermes-dashboard-setup-went-sideways"></a>
Untangling the Homelab After a Hermes Dashboard Setup Went Sideways
</h1><p><strong>Subtitle:</strong> We tried to expose one dashboard, tripped over old routing and auth tech debt, broke a few things, then used the incident to make the stack less cursed.</p>
<div class="gallery-wall" id="photoWall-b63e55057"></div>
<script>
  (function() {
    const imageUrls = JSON.parse("[\"/posts/hermes-litellm-authelia-control-plane/images/icons/traefik-proxy.svg\",\"/posts/hermes-litellm-authelia-control-plane/images/icons/cloudflare.svg\",\"/posts/hermes-litellm-authelia-control-plane/images/icons/docker.svg\",\"/posts/hermes-litellm-authelia-control-plane/images/icons/authelia.svg\",\"/posts/hermes-litellm-authelia-control-plane/images/icons/litellm.svg\"]");
    let images = [];
    const wallId = '#photoWall-b63e55057';
    let resizeTimer;
    let lastWidth = 0;
    
    function init() {
      createInitialDOM();
      loadImages();
      const wall = _$(wallId);
      if (wall && window.ResizeObserver) {
        const observer = new ResizeObserver((entries) => {
          const newWidth = entries[0].contentRect.width;
          if (Math.abs(newWidth - lastWidth) > 1) {
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(renderPhotoWall, 250);
          }
        });
        observer.observe(wall);
      } else {
        window.addEventListener('resize', handleResize);
      }
    }
    
    function createInitialDOM() {
      const wall = _$(wallId);
      if (!wall || !imageUrls.length) return;
      
      wall.style.opacity = '0';
      wall.style.pointerEvents = 'none';
      
      imageUrls.forEach((url) => {
        images.push({
          width: 800,
          height: 600,
          aspectRatio: 4/3,
          url: url,
          loaded: false
        });
      });
      
      renderPhotoWall();
    }
    
    function loadImages() {
      const loadPromises = imageUrls.map((url, index) => {
        return new Promise((resolve, reject) => {
          const img = new Image();
          img.onload = () => {
            if (images[index]) {
              images[index].width = img.naturalWidth;
              images[index].height = img.naturalHeight;
              images[index].aspectRatio = img.naturalWidth / img.naturalHeight;
              images[index].loaded = true;
            }
            resolve();
          };
          img.onerror = () => {
            if (images[index]) {
              images[index].loaded = true;
            }
            resolve();
          };
          img.src = url;
        });
      });
      
      Promise.all(loadPromises)
        .then(() => {
          const wall = _$(wallId);
          if (wall) {
            wall.style.opacity = '1';
            wall.style.pointerEvents = 'auto';
            wall.style.transition = 'opacity 0.3s ease';
          }
          renderPhotoWall();
        })
        .catch((error) => console.error("图片加载失败:", error));
    }
    
    function renderPhotoWall() {
      const wall = _$(wallId);
      
      if (!images.length || !wall) return;
      
      const screenWidth = wall.offsetWidth || window.innerWidth - 40;
      lastWidth = screenWidth;
      
      wall.innerHTML = "";
      const rowHeight = 200;
      const gap = 10;
      const maxPerRow = 4;
      
      let row = [];
      let rowWidth = 0;
      const rows = [];
      
      images.forEach((img) => {
        const scaledWidth = rowHeight * img.aspectRatio;
        
        if (rowWidth + scaledWidth <= screenWidth && row.length < maxPerRow) {
          row.push(img);
          rowWidth += scaledWidth + gap;
        } else {
          if (row.length) rows.push(row);
          row = [img];
          rowWidth = scaledWidth + gap;
        }
      });
      
      if (row.length) rows.push(row);
      
      rows.forEach((row) => {
        const rowDiv = document.createElement("div");
        rowDiv.style.cssText = `display: flex; gap: ${gap}px; width: 100%`;
        
        row.forEach((img) => {
          const item = document.createElement("div");
          item.className = "photo-item";
          const flex = row.length === 1 ? '1' : img.aspectRatio;
          item.style.cssText = `flex: ${flex}; height: ${rowHeight}px`;
          
          const imgEl = document.createElement("img");
          imgEl.alt = "Gallery Image";
          imgEl.style.cssText = "width: 100%; height: 100%; object-fit: cover";
          imgEl.className = "lazyload";
          imgEl.dataset.src = img.url;
          imgEl.dataset.sizes = "auto";

          const link = document.createElement("a");
          link.href = img.url;
          link.dataset.pswpWidth = img.width;
          link.dataset.pswpHeight = img.height;
          link.target = "_blank";
          link.className = "article-gallery-item";
          link.style.cssText = "width: 100%; height: 100%";
          link.appendChild(imgEl);
          
          item.appendChild(link);
          rowDiv.appendChild(item);
        });
        
        wall.appendChild(rowDiv);
      });
    }
    
    function handleResize() {
      clearTimeout(resizeTimer);
      resizeTimer = setTimeout(renderPhotoWall, 250);
    }
    
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', init);
    } else {
      init();
    }
  })();
</script><link rel="preload" as="style" href="/css/gallery.min.94cd3998ffbf0abe50a0f8a9251c39f9df60899a02db59add20026d894f2d44f.css" onload="this.onload=null;this.rel='stylesheet'" />
<p>This was not supposed to become an infrastructure incident.</p>
        
        <hr><p>Published on 2026-06-03 at <a href='https://umi4.life/'>UmiLife's Blog</a>, last modified on 2026-06-03</p>]]></description><category>homelab</category><category>infrastructure</category><category>automation</category></item></channel></rss>