<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Portainer on Margrop Blog</title>
    <link>https://blog.margrop.net/en/tag/portainer/</link>
    <description>Recent content in Portainer on Margrop Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-US</language>
    <lastBuildDate>Fri, 10 Jul 2026 15:30:00 +0800</lastBuildDate>
    <atom:link href="https://blog.margrop.net/en/tag/portainer/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>I Put Docker in a Control Room: Portainer CE 2.39.4 Deployment, Daily Use, and Real Traps</title>
      <link>https://blog.margrop.net/en/post/portainer-ce-docker-deployment-guide/</link>
      <pubDate>Fri, 10 Jul 2026 15:30:00 +0800</pubDate>
      <guid>https://blog.margrop.net/en/post/portainer-ce-docker-deployment-guide/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Bottom line first&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Portainer does not replace Docker. It is a control room for a Docker host. Docker Engine remains the machinery; Portainer organizes containers, images, networks, volumes, and Compose stacks into a web dashboard.&lt;/p&gt;&#xA;&lt;p&gt;I ran an isolated deployment of &lt;code&gt;portainer/portainer-ce:2.39.4&lt;/code&gt;, initialized it, connected the local Docker environment, inspected the dashboard, filtered a disposable container, and created a demonstration stack. The installation is one &lt;code&gt;docker run&lt;/code&gt; command. The important lessons are broader: persist &lt;code&gt;/data&lt;/code&gt;, understand that &lt;code&gt;/var/run/docker.sock&lt;/code&gt; is highly privileged, and never expose the management interface to an untrusted network without protection.&lt;/p&gt;&#xA;&lt;p&gt;This article contains no complete IP address, real host name, private domain, administrator password, token, cookie, private registry address, or production container name. The screenshots use disposable names, and the container address is redacted.&lt;/p&gt;&#xA;&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>Stop Letting Agents Burn Tokens: Wiring Headroom into NewAPI, OpenClaw, and HermesAgent</title>
      <link>https://blog.margrop.net/en/post/headroom-newapi-openclaw-hermesagent-token-compression-guide/</link>
      <pubDate>Sat, 20 Jun 2026 12:30:00 +0800</pubDate>
      <guid>https://blog.margrop.net/en/post/headroom-newapi-openclaw-hermesagent-token-compression-guide/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;I did not replace NewAPI, and I did not point OpenClaw or HermesAgent at an unproven gateway. The actual design is simpler: place Headroom in front of NewAPI, then move only the already validated OpenAI-compatible providers to &lt;code&gt;http://&amp;lt;headroom-host&amp;gt;:8787/v1&lt;/code&gt;. The original NewAPI endpoint stays available. Long agent context now goes through Headroom first, gets compressed, then continues to NewAPI for the same routing and model management as before.&lt;/p&gt;&#xA;&lt;p&gt;The rule that kept the rollout boring was: &lt;strong&gt;test first, edit second; migrate only OpenAI-compatible providers that pass; leave non-OpenAI fallbacks alone.&lt;/strong&gt; This post is both a write-up and a runbook you can hand to an Agent or follow manually.&lt;/p&gt;&#xA;&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>I dockerized AnythingLLM and the container turned into a wound-up squirrel — restarting forever until I handed it that magic 1000:1000</title>
      <link>https://blog.margrop.net/en/post/anythingllm-docker-deploy/</link>
      <pubDate>Wed, 17 Jun 2026 20:30:00 +0800</pubDate>
      <guid>https://blog.margrop.net/en/post/anythingllm-docker-deploy/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;AnythingLLM, Mintplex Labs&amp;rsquo; &amp;ldquo;stuff-anything-in-a-LLM&amp;rdquo; desktop, ships an official Docker image. In the happy path one &lt;code&gt;docker run&lt;/code&gt; is all you need. But the in-container user &lt;code&gt;anythingllm&lt;/code&gt; is picky: the host directory you bind-mount has to be owned by &lt;code&gt;1000:1000&lt;/code&gt;, otherwise it can&amp;rsquo;t write its own SQLite file, the Prisma migration step on startup crashes, and the container enters a &lt;em&gt;restart-crash-restart&lt;/em&gt; loop. The fix takes under a minute: &lt;code&gt;chown -R 1000:1000 /your/data/dir&lt;/code&gt;, then &lt;code&gt;docker compose up -d&lt;/code&gt; again, and it dutifully listens on &lt;code&gt;3001&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ll also explain why Prisma&amp;rsquo;s &lt;code&gt;file:../storage/anythingllm.db&lt;/code&gt; relative path is a footgun, give you a Portainer-ready stack file, and finish with a Q&amp;amp;A on the most common gotchas.&lt;/p&gt;&#xA;&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>The Portainer 500 Error That Wasn&#39;t the YAML — A Two-librespeed_default-Network Story</title>
      <link>https://blog.margrop.net/en/post/portainer-500-duplicate-compose-network-enigma/</link>
      <pubDate>Sat, 13 Jun 2026 08:00:00 +0800</pubDate>
      <guid>https://blog.margrop.net/en/post/portainer-500-duplicate-compose-network-enigma/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;The short version&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;You update a stack in Portainer. The browser slaps you with a red &lt;code&gt;500 Internal Server Error&lt;/code&gt;. You assume the YAML is wrong, fix the indentation, swap the quotes, drop the image tag. You hit Update again. Same 500. And again. And again.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;The real culprit is buried at the deepest level of the HTTP response body: &lt;code&gt;network librespeed_default is ambiguous (2 matches found on name)&lt;/code&gt;.&lt;/strong&gt; Two networks with the same name exist in the same Docker engine—two IDs, two &lt;code&gt;Created&lt;/code&gt; timestamps, two &lt;code&gt;com.docker.compose.config-hash&lt;/code&gt; labels, but a single shared name. Compose asks the engine to look up that name; the engine refuses to pick between them; &lt;code&gt;compose up&lt;/code&gt; fails; Portainer wraps the error as a 500 and returns it to your browser.&lt;/p&gt;&#xA;&lt;p&gt;The fix is embarrassingly simple: &lt;strong&gt;delete one of the two orphan networks&lt;/strong&gt; (&lt;code&gt;docker network rm &amp;lt;id&amp;gt;&lt;/code&gt; or click Remove in Portainer&amp;rsquo;s Networks page), then re-run Update the stack with the exact same content. It just works.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;This post is a real debugging session: a stack update that was supposed to be a 30-second mount-path change turned into a 1-hour investigation. All private details (internal addresses, registry URLs, volume paths, container names, credentials) have been replaced with &lt;code&gt;&amp;lt;PLACEHOLDER&amp;gt;&lt;/code&gt;. Only public source code, official docs, and the error text itself are preserved.&lt;/p&gt;</description>
    </item>
    <item>
      <title>When Docker Networks Collide with the Home LAN: Moving Auto-Allocated Subnets Back to 172</title>
      <link>https://blog.margrop.net/en/post/docker-network-subnet-conflict-migration-record/</link>
      <pubDate>Mon, 13 Apr 2026 08:00:00 +0800</pubDate>
      <guid>https://blog.margrop.net/en/post/docker-network-subnet-conflict-migration-record/</guid>
      <description>&lt;p&gt;This post records a very practical Docker networking failure: the number of containers kept growing, Docker&amp;rsquo;s auto-assigned user-defined bridge networks eventually started landing in &lt;code&gt;192.168.x.x&lt;/code&gt;, and those networks began colliding with the real home LAN. The symptoms were not dramatic at first. Nothing looked “broken” in the usual sense. Containers were still running, the host was still alive, and the dashboard still looked normal. But parts of the home network became unreliable, and once I dug into it on my NAS, the root cause turned out to be the Docker network allocation strategy rather than any single bad container.&lt;/p&gt;&#xA;&lt;p&gt;The fix was straightforward in principle, but important in practice: I moved Docker&amp;rsquo;s automatic network pool to &lt;code&gt;172.16.0.0/12&lt;/code&gt;, migrated the existing &lt;code&gt;192.168.*&lt;/code&gt; networks onto the 172 private range, and made sure future networks created through Portainer, Dockhand, or plain &lt;code&gt;docker compose&lt;/code&gt; would follow the same rule.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
