<?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: flisk</title>
    <description>The latest articles on DEV Community by flisk (@flisk).</description>
    <link>https://dev.to/flisk</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%2F4145557%2F6879342a-c388-4b5e-818b-aa736b779196.png</url>
      <title>DEV Community: flisk</title>
      <link>https://dev.to/flisk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flisk"/>
    <language>en</language>
    <item>
      <title>Saylor Took the Classroom Offline for PHP2Shell</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:26:20 +0000</pubDate>
      <link>https://dev.to/flisk/saylor-took-the-classroom-offline-for-php2shell-5g84</link>
      <guid>https://dev.to/flisk/saylor-took-the-classroom-offline-for-php2shell-5g84</guid>
      <description>&lt;p&gt;Saylor Academy did not discover a breach in a learner account. It found PHP under the classroom, and it pulled the plug.&lt;/p&gt;

&lt;p&gt;On 21 October the team emailed learners from &lt;a href="mailto:notices@saylor.org"&gt;notices@saylor.org&lt;/a&gt;. A serious flaw in PHP, the software behind the sites, had been announced that day. To protect accounts, Saylor Learn (learn.saylor.org) and Saylor University (degrees.saylor.org) would go offline while the hosting partner installed the fix.&lt;/p&gt;

&lt;p&gt;The window in that email is 6:00pm to 10:00am Eastern Daylight Time, with a return expected by 10:00am EDT on Thursday 22 October. A public Saylor page has also shown 8:00pm to 10:00am EDT. Confirm the live banner on learn.saylor.org before you treat either clock as final.&lt;/p&gt;

&lt;h2&gt;
  
  
  What learners actually lost
&lt;/h2&gt;

&lt;p&gt;For those hours you cannot sign in, open a course, take a quiz or exam, or download a certificate. Progress, grades, and certificates stay put. Nothing completed is wiped. If you are mid-quiz, the email says submit it before the window starts. If the work runs long, the update goes on the maintenance page at learn.saylor.org.&lt;/p&gt;

&lt;p&gt;That is the whole incident, and it is the point. The scary sentence in a security mail is usually "we were hacked." This one is "you cannot get in, because we would rather be down than exposed."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a school is in this series
&lt;/h2&gt;

&lt;p&gt;PHP2Shell is the public name for CVE-2026-109618 and CVE-2026-109619. PHP rates each 9.2. Both can allow unauthenticated remote code execution on a PHP install that is serving web requests. Fixes landed on 21 October in PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35. PHP's own line is that updating is the only complete fix.&lt;/p&gt;

&lt;p&gt;Saylor is not a hyperscaler. It is a free course platform with quizzes, exams, and certificates. That is exactly the data you do not leave on an unpatched runtime while a proof of concept is already circulating. The host installs the package. Saylor owns the outage message. Learners own the inconvenience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The local version of the same mail
&lt;/h2&gt;

&lt;p&gt;If your app is PHP and the image tag is older than those four releases, you are in the same bucket as learn.saylor.org. A laptop container is not exempt once it is reachable. Bump the tag, restart the workers, and do not call it patched until the new binary is the one answering requests.&lt;/p&gt;

&lt;p&gt;Old workers keep the old code. That is the step people skip, and it is why a maintenance page exists at all.&lt;/p&gt;

&lt;p&gt;Sources: Saylor learner notice, 21 October 2026; &lt;a href="https://www.php.net/archive/2026.php#2026-10-21-5" rel="noopener noreferrer"&gt;PHP security advisory&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>webdev</category>
      <category>news</category>
    </item>
    <item>
      <title>DigiD Went Dark on Purpose. Logius took DigiD, MijnOverheid, and Overheid.nl offline on 21 October. It was a precaution, not a confirmed intrusion.</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:26:00 +0000</pubDate>
      <link>https://dev.to/flisk/digid-went-dark-on-purpose-logius-took-digid-mijnoverheid-and-overheidnl-offline-on-21-october-7da</link>
      <guid>https://dev.to/flisk/digid-went-dark-on-purpose-logius-took-digid-mijnoverheid-and-overheidnl-offline-on-21-october-7da</guid>
      <description>&lt;p&gt;The Dutch government did not wait to see if PHP2Shell was theoretical. It turned off the sites people use to prove who they are.&lt;/p&gt;

&lt;p&gt;On Wednesday 21 October, Rijksoverheid said a number of government sites were taken offline as a precaution after two critical flaws in PHP. The list it named included Overheid.nl, MijnOverheid.nl, and DigiD. The NCSC had not, at that point, seen signs the flaws were being exploited. The sites still went down. Logius, which runs MijnOverheid, confirmed the shutdown. NRC also reported the Tweede Kamer site offline the same day.&lt;/p&gt;

&lt;p&gt;Read that again. The identity portal went dark before the patch, not after a breach notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What stayed up, and what did not
&lt;/h2&gt;

&lt;p&gt;Reporting through the day was messy, which is normal when the website and the login protocol are different things. The DigiD website was unreachable. Some reports said you could still sign in with DigiD on other sites that were still online. The app was partly down. By Wednesday evening, MijnOverheid and then DigiD.nl were reported back. Overheid.nl was still cited as down in at least one late update.&lt;/p&gt;

&lt;p&gt;Do not publish this piece with "DigiD is down" in the present tense. Stamp it. As of Wednesday evening, 21 October, the precautionary outage had started in the morning and the identity sites were coming back after PHP's fixes landed around 16:00 Dutch time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sentence the NCSC used
&lt;/h2&gt;

&lt;p&gt;This was not sold as an attack. A Rijksoverheid update said the patches were available and that site owners should apply them as fast as possible. The earlier advice was to weigh the risk and, if needed, take the site offline until the update existed. PHP had told users to prepare for a release at 14:00 UTC. Dutch time, that was 16:00.&lt;/p&gt;

&lt;p&gt;That is the operational lesson. A CVSS 9.2 in the language under the site is not a ticket you schedule for Friday if the site is DigiD. You eat the outage, you install 8.5.12, 8.4.27, 8.3.36, or 8.2.35, you restart the workers, and you bring the hostname back only after the new binary is the one serving.&lt;/p&gt;

&lt;p&gt;Hospitals, municipalities, RTL, and council-information platforms were in the same Dutch wave. They get their own mentions later if this series keeps going. This post is about the org that had to explain to the whole country why the login page was a maintenance screen.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://www.rijksoverheid.nl/actueel/nieuws/2026/10/21/aantal-websites-rijksoverheid-preventief-offline-na-kwetsbaarheden-php" rel="noopener noreferrer"&gt;Rijksoverheid, 21 October 2026&lt;/a&gt;; &lt;a href="https://www.nrc.nl/nieuws/2026/10/21/steeds-meer-websites-waaronder-digid-en-mijnoverheid-nl-offline-gehaald-vanwege-lekken-in-programmeertaal-php-a4939457" rel="noopener noreferrer"&gt;NRC&lt;/a&gt;; &lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;PHP advisory&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>news</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cygames Scheduled the Outage for the Patch, Then Moved It Earlier</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:25:28 +0000</pubDate>
      <link>https://dev.to/flisk/cygames-scheduled-the-outage-for-the-patch-then-moved-it-earlier-298h</link>
      <guid>https://dev.to/flisk/cygames-scheduled-the-outage-for-the-patch-then-moved-it-earlier-298h</guid>
      <description>&lt;p&gt;Cygames did not bury the cause in "emergency maintenance." The posts named PHP.&lt;/p&gt;

&lt;p&gt;On 21 October the company said Umamusume: Pretty Derby, Granblue Fantasy, Shadowverse: Worlds Beyond, and other live titles would go down so the servers could be made safe. The Umamusume account told players they would not be able to start or play during the work. Famitsu reported the original window as about 23:00 Japan time on the 21st, lined up with the PHP fix publication expected that hour. Granblue was planned through 07:00 on 22 October, with a warning the end time could move. Umamusume and Shadowverse did not publish an end time.&lt;/p&gt;

&lt;p&gt;Then the start moved. A later Umamusume notice pulled maintenance forward to about 22:00, and said it might move again. That is what a broken embargo looks like from the operator side. You planned the outage for the patch. The patch, and the exploit talk, showed up early. You start early.&lt;/p&gt;

&lt;h2&gt;
  
  
  One company, three clocks
&lt;/h2&gt;

&lt;p&gt;This is a useful contrast with Saylor, which had one host and one overnight window, and with FFXIV, which froze new logins and left existing sessions alone. Cygames had several games on the same language bug and did not pretend they would return together. Granblue got a planned end. The others got "we will tell you." That is better than a fake 7:00am that slips for six hours while players refresh a status page.&lt;/p&gt;

&lt;p&gt;The fix they were waiting on is the same four releases as everyone else: PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35, from the 21 October security release. A game server that still answers on an older worker after that release is not "in maintenance." It is late.&lt;/p&gt;

&lt;p&gt;If you operate more than one PHP front, write the notice per product. Shared cause, separate end times, and a sentence that the start can move if the patch moves. Players can forgive a dark game. They do not forgive a countdown you already knew was fiction.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://www.famitsu.com/article/202610/91632" rel="noopener noreferrer"&gt;Famitsu, 21 October 2026&lt;/a&gt;; &lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;PHP advisory&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>gamedev</category>
      <category>news</category>
    </item>
    <item>
      <title>Final Fantasy XIV Closed the Door, Not the World</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:25:05 +0000</pubDate>
      <link>https://dev.to/flisk/final-fantasy-xiv-closed-the-door-not-the-world-18g</link>
      <guid>https://dev.to/flisk/final-fantasy-xiv-closed-the-door-not-the-world-18g</guid>
      <description>&lt;p&gt;Square Enix did not have to take Eorzea offline to take PHP offline. It stopped new logins.&lt;/p&gt;

&lt;p&gt;On 21 October the Lodestone notice for Final Fantasy XIV said new logins were temporarily suspended across all worlds to address a critical vulnerability in PHP, the language hitting systems worldwide. Secondary reports the same day said people already logged in could keep playing, and that there was no public return time on the notice.&lt;/p&gt;

&lt;p&gt;That split is the story. A game login service is a web front on a live world. You can refuse the next authentication without dumping every character who is already past the gate. It is still an outage. Anyone who disconnected, anyone on a new session, anyone who needed the web layer, was stuck. It is a narrower outage than "the game is gone," and it is a more honest one than leaving the login form up on a runtime PHP had just called unsafe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;p&gt;This post is not a claim that Square Enix was breached through PHP2Shell. The notice that was public on 21 October is a maintenance action tied to the PHP flaw. Account-security warnings from earlier in the month are a different incident. Do not merge them in the headline.&lt;/p&gt;

&lt;p&gt;PHP's fixes that day were 8.5.12, 8.4.27, 8.3.36, and 8.2.35. Until those, or a vendor build that contains the same fix, are the binaries behind the login service, a suspended login is the correct product decision. Opening the form early because players are angry is how a maintenance window becomes an incident report.&lt;/p&gt;

&lt;p&gt;If you ship a game, a store, or a course site with a PHP login in front of a system that can survive without new sessions, copy this shape. Freeze new auth. Say why in one sentence. Do not invent an end time you do not have. Bring the form back only after the worker restart, not after the package download.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://na.finalfantasyxiv.com/lodestone/news/detail/682d63eac6bb6c380a0b1b16bbeb0efd45c13ad5" rel="noopener noreferrer"&gt;FINAL FANTASY XIV Lodestone notice, 21 October 2026&lt;/a&gt;; &lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;PHP advisory&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>gamedev</category>
      <category>news</category>
    </item>
    <item>
      <title>Wikipedia Stayed Up. Toolforge Did Not.</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:24:42 +0000</pubDate>
      <link>https://dev.to/flisk/wikipedia-stayed-up-toolforge-did-not-5h8o</link>
      <guid>https://dev.to/flisk/wikipedia-stayed-up-toolforge-did-not-5h8o</guid>
      <description>&lt;p&gt;The Wikimedia Foundation did not switch Wikipedia off and go home. It switched off the parts where it could not promise the PHP version.&lt;/p&gt;

&lt;p&gt;On 21 October the foundation said it was dealing with two critical PHP flaws that hit any site running PHP, not just Wikimedia. Readers might see wiki outages during the work. Supporting services were taken offline on purpose until they could be patched. The names that matter are Toolforge and Cloud VPS. Those host a lot of PHP apps. Volunteer developers run them. Some of those users pick their own PHP version. Staff cannot safely flip that back on by pushing one package.&lt;/p&gt;

&lt;p&gt;A later community status page told the same story in a list. Cloud VPS and Toolforge, including &lt;code&gt;*.wmcloud.org&lt;/code&gt;, &lt;code&gt;*.wmflabs.org&lt;/code&gt;, and &lt;code&gt;*.toolforge.org&lt;/code&gt;, were offline and might stay that way longer. Wikibase Cloud was down. Phabricator, translatewiki.net, GitLab, and doc.wikimedia.org were later marked restored. Zuul's status page stayed down for the same reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  The outage is an ownership problem
&lt;/h2&gt;

&lt;p&gt;This is the post in the series that is not about a vendor installing a patch overnight. Saylor could hand the window to a host. Logius could take DigiD down and bring it back. Wikimedia can patch what it operates. It cannot patch a Toolforge tool whose owner chose the runtime.&lt;/p&gt;

&lt;p&gt;That is why the foundation's note says some of those services may be offline longer, and why staff were triaging, emailing documented owners, and upgrading only where they had access. A security bug in core PHP becomes an inventory bug the moment more than one team can pin a version.&lt;/p&gt;

&lt;p&gt;If you run a platform where users bring their own PHP, you are in this post, not the Saylor post. Taking the platform offline is the honest move until every reachable worker is on 8.5.12, 8.4.27, 8.3.36, or 8.2.35, or on a vendor backport you have actually installed. "We patched the main wiki" is not a status for the side hostnames.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://diff.wikimedia.org/2026/10/21/impact-of-php-vulnerabilities-on-wikimedia-tools-and-services/" rel="noopener noreferrer"&gt;Wikimedia Diff, 21 October 2026&lt;/a&gt;; &lt;a href="https://wikitech.wikimedia.org/wiki/Incidents/2026-10-21_PHP_security_update/Community" rel="noopener noreferrer"&gt;Wikitech community status&lt;/a&gt;; &lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;PHP advisory&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>opensource</category>
      <category>news</category>
    </item>
    <item>
      <title>Pokémon Home Went Down With the PHP Patch</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:24:27 +0000</pubDate>
      <link>https://dev.to/flisk/pokemon-home-went-down-with-the-php-patch-2fg2</link>
      <guid>https://dev.to/flisk/pokemon-home-went-down-with-the-php-patch-2fg2</guid>
      <description>&lt;p&gt;Pokémon did not get a neat single-paragraph post in the first wave of PHP2Shell coverage. It got a list, and the list is still worth a post if you label it as a list.&lt;/p&gt;

&lt;p&gt;On 21 October, GoNintendo and Dexerto reported that Pokémon Home, Pokémon Champions, and Pokémon Sleep were offline, and that the official Pokémon Japan site was down with them. The cause they gave was the same pair of critical PHP flaws that had already pulled Umamusume, Final Fantasy XIV logins, and a pile of government sites. I have not opened a first-party Pokémon notice for this draft. If you have the official post, swap this paragraph for a quote and a link before you publish. If you cannot find one, say "reported" in the first sentence and do not upgrade it to "Pokémon confirmed."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it still belongs in the six
&lt;/h2&gt;

&lt;p&gt;Home is not a marketing site. It is the box where creatures, and the account around them, live. Sleep and Champions are live services with logins. Taking those down on patch day is the same decision Saylor made for certificates and Logius made for DigiD: the data is worth more than the uptime, and the bug is in the language, not in one route you can feature-flag off.&lt;/p&gt;

&lt;p&gt;PHP's position on 21 October was blunt. Every PHP install serving web requests was in scope. The highest risk they called out was mod_php, where an unauthenticated request can hit the bug before application code runs. A game studio cannot audit its way out of that in an afternoon. It can pull the service, install 8.5.12, 8.4.27, 8.3.36, or 8.2.35, restart the workers, and reopen.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to end the series
&lt;/h2&gt;

&lt;p&gt;Six orgs, one pattern. Saylor told learners the grades were safe and the login was not. Logius told a country the identity site was a precaution. Wikimedia could patch its own stack and still had to darken Toolforge. Square Enix froze new sessions. Cygames moved the start time when the patch moved. Pokémon's live services showed up on the same day, in the same bucket.&lt;/p&gt;

&lt;p&gt;None of these posts are a breach report. Do not write them as one. The publishable fact is the outage, the clock, and the version that has to be running before the hostname comes back.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://gonintendo.com/contents/66345-multiple-pokemon-games-taken-offline-temporarily-due-to-php-vulnerability-concerns" rel="noopener noreferrer"&gt;GoNintendo, 21 October 2026&lt;/a&gt;; &lt;a href="https://www.dexerto.com/pokemon/pokemon-umamusume-and-other-major-games-taken-offline-over-critical-php-vulnerability-3420823/" rel="noopener noreferrer"&gt;Dexerto&lt;/a&gt;; &lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;PHP advisory&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>gamedev</category>
      <category>news</category>
    </item>
    <item>
      <title>Three Sentences in This Repo Cannot All Be True</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:03:15 +0000</pubDate>
      <link>https://dev.to/flisk/three-sentences-in-this-repo-cannot-all-be-true-1ejg</link>
      <guid>https://dev.to/flisk/three-sentences-in-this-repo-cannot-all-be-true-1ejg</guid>
      <description>&lt;p&gt;A small project can survive a missing feature. It has a harder time surviving two docs that describe different products. Flisk currently has that problem in public. If you are about to write a tutorial from one page, read this first, then generate the files and believe those.&lt;/p&gt;

&lt;p&gt;The binary is the smallest version of the bug. The README says &lt;code&gt;npm install -g @flisk-sh/flisk&lt;/code&gt; installs the &lt;code&gt;tdk&lt;/code&gt; command. The Helm page and the comparison page use &lt;code&gt;tdk&lt;/code&gt;. The feature guide uses &lt;code&gt;flisk&lt;/code&gt;. Run &lt;code&gt;tdk --version&lt;/code&gt; and &lt;code&gt;flisk --version&lt;/code&gt;. Quote the one that exists on your machine. Do not quote both as if they were a style choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The database is not allowed to have two default names
&lt;/h2&gt;

&lt;p&gt;The feature guide says the project database is &lt;code&gt;app_dev&lt;/code&gt; by default, and that backends use &lt;code&gt;DATABASE_URL&lt;/code&gt;. The scope note, read from a scaffolded backend's generated Compose file and &lt;code&gt;.env&lt;/code&gt; in Flisk 1.3.86, says one shared Postgres 16 container and one database per stack, named &lt;code&gt;&amp;lt;project&amp;gt;_&amp;lt;stack&amp;gt;&lt;/code&gt;, with the password in &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those sentences cannot both be the default. One of them is stale, or they describe different layers and nobody wrote the layer down. Before you put either name in application code, print the URL the container actually received. Then keep that name out of source anyway. The earlier piece in this series is the rule: a local database name is a harness fact, not a constant.&lt;/p&gt;

&lt;p&gt;The comparison page also says Flisk provisions NATS. The feature guide's always-on list is proxy and database-management. NATS is real enough to have had a bug: a changelog entry says the focus filter left &lt;code&gt;nats&lt;/code&gt; disabled, so a service with the NATS feature retried its broker until someone ran &lt;code&gt;tilt enable nats&lt;/code&gt; by hand. "Automatically provisioned" and "disabled until you enable it" are different products. If your service needs a broker, check &lt;code&gt;tilt&lt;/code&gt; after &lt;code&gt;up&lt;/code&gt;. Do not assume the intro paragraph started it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prisma is not a default that is also opt-in
&lt;/h2&gt;

&lt;p&gt;The feature guide shows a backend example with &lt;code&gt;"features": ["prisma"]&lt;/code&gt; in the same breath as the default feature list. Later it says Prisma is not enabled by default. Add &lt;code&gt;"prisma"&lt;/code&gt; only together with &lt;code&gt;prisma/schema.prisma&lt;/code&gt; and the &lt;code&gt;prisma&lt;/code&gt; and &lt;code&gt;@prisma/client&lt;/code&gt; dependencies. The generated Dockerfile copies &lt;code&gt;prisma/&lt;/code&gt; and runs &lt;code&gt;prisma generate&lt;/code&gt;. Without those files, the image fails to build.&lt;/p&gt;

&lt;p&gt;That second paragraph is the one to follow. The example is how a tutorial creates a red build and calls the tool broken. Enabling a feature flag is not the same as owning the schema the flag expects.&lt;/p&gt;

&lt;p&gt;The same page puts &lt;code&gt;golden_image&lt;/code&gt; inside an "optional, disabled by default" snippet and then says golden-image is on by default. The troubleshoot page is clearer about the consequence either way: &lt;code&gt;&amp;lt;project&amp;gt;-l1:latest&lt;/code&gt; and the other golden tags are local images. They are not in a registry. A CI job that has not built them cannot build &lt;code&gt;.autogenerated/Dockerfile.app.autogenerated&lt;/code&gt;. Turn the feature off only if you know which Dockerfile you are about to own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doctor used to lie about a free port
&lt;/h2&gt;

&lt;p&gt;One contradiction already got fixed, which is why the class of bug is worth knowing. &lt;code&gt;tdk doctor&lt;/code&gt; checks that host ports 80, 443, and 5432 are free and names what holds them, often a local Postgres. A changelog entry says the check used to bind &lt;code&gt;0.0.0.0&lt;/code&gt; and &lt;code&gt;127.0.0.1&lt;/code&gt; at the same time. On Linux the second bind collided with the first, so a free port was reported taken. It now probes them one after the other.&lt;/p&gt;

&lt;p&gt;If an old doctor told you port 80 was busy and nothing was listening, that report can be the bug, not the machine. Re-run doctor after updating. On Linux and WSL2 a non-root user still cannot bind port 80, so &lt;code&gt;up&lt;/code&gt; may move Traefik to the fallback range and print a URL with a port. That part is not a lie. The printed URL is the URL.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tdk doctor&lt;/code&gt; also cannot see a chart. The Helm page says it checks local readiness and service issues, not production configuration. A Docker Desktop window can be open while the engine is not responding. Doctor's own hint, from the L1 troubleshoot page, is to quit Docker Desktop completely, reopen it, and wait until it is running. A whale icon is not a daemon.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to quote, until the pages agree
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you are about to write&lt;/th&gt;
&lt;th&gt;Quote this&lt;/th&gt;
&lt;th&gt;Do not quote this&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The database name&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;DATABASE_URL&lt;/code&gt; in the generated env for that stack&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;app_dev&lt;/code&gt; or &lt;code&gt;&amp;lt;project&amp;gt;_&amp;lt;stack&amp;gt;&lt;/code&gt; from memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether NATS is up&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tilt&lt;/code&gt; after &lt;code&gt;up&lt;/code&gt;, for that feature&lt;/td&gt;
&lt;td&gt;The comparison intro alone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether Prisma is on&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;features&lt;/code&gt; array, plus a schema and the two packages&lt;/td&gt;
&lt;td&gt;A sample manifest labeled "default"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which command to document&lt;/td&gt;
&lt;td&gt;The binary &lt;code&gt;--version&lt;/code&gt; printed&lt;/td&gt;
&lt;td&gt;Whichever page you had open&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether the chart is valid&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;helm lint&lt;/code&gt; and &lt;code&gt;helm template&lt;/code&gt; on the pinned chart&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tdk doctor&lt;/code&gt; or &lt;code&gt;tdk config verify&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether port 80 is free&lt;/td&gt;
&lt;td&gt;A current doctor, then the URL &lt;code&gt;up&lt;/code&gt; printed&lt;/td&gt;
&lt;td&gt;A remembered "port taken" from an old binary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Optional infra has a quieter trap of the same family. Monitoring, ELK, and Debezium are free and off by default. The feature guide marks monitoring and ELK as high resource. Enabling them because the page listed them is how a 16 GB laptop stops being a daily driver. Golden layers are the optional-looking feature that is already on. ELK is the default-looking cost that is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The size of a contradiction
&lt;/h2&gt;

&lt;p&gt;None of these are the product. The product is still a manifest, a local stack, and a chart you write yourself. The docs are part of the surface area, though, and an agent will treat the first page it opens as the spec.&lt;/p&gt;

&lt;p&gt;When two pages disagree, generate the file or run the command. Then fix the page that lost. A tutorial that picks a side without that check is how the third sentence appears.&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>devops</category>
      <category>docker</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Do Not Boot a Cluster to Change a Button</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:02:59 +0000</pubDate>
      <link>https://dev.to/flisk/do-not-boot-a-cluster-to-change-a-button-3ll0</link>
      <guid>https://dev.to/flisk/do-not-boot-a-cluster-to-change-a-button-3ll0</guid>
      <description>&lt;p&gt;A local cluster feels like rigor. Often it is a tax.&lt;/p&gt;

&lt;p&gt;kind and minikube give you a Kubernetes API on the laptop. They do not give you an inner loop. Helm, kubectl, Skaffold, or Tilt still has to sit on top. Flisk's comparison doc draws the line in the place most tool pages smudge it: if your software talks to that API, you need the cluster. If your software is a service, a page, a worker, or an agent, the cluster is an operational target, not a dependency of the edit you are making.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the cluster is mandatory
&lt;/h2&gt;

&lt;p&gt;Refuse Flisk, and refuse "just use Compose," when the thing you are building is the cluster:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an operator, a CRD, or a reconciler&lt;/li&gt;
&lt;li&gt;an admission webhook&lt;/li&gt;
&lt;li&gt;ServiceAccount RBAC you must see fail&lt;/li&gt;
&lt;li&gt;a Pod disruption budget, a NetworkPolicy, a CNI plugin&lt;/li&gt;
&lt;li&gt;a service mesh&lt;/li&gt;
&lt;li&gt;a Helm chart or Kustomize overlay whose job is to install cleanly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are not local-dev preferences. A Docker network will not grow a kube-apiserver because you wanted parity. Skaffold is the better inner loop when the same pipeline should &lt;code&gt;dev&lt;/code&gt; against a cluster and &lt;code&gt;run&lt;/code&gt; in CI. DevSpace is the better inner loop when the code should execute inside a remote pod or a vcluster, especially if you need the file sync to run both ways.&lt;/p&gt;

&lt;p&gt;Native Windows without WSL2 is a harder stop than a philosophy. The comparison doc says &lt;code&gt;flisk up&lt;/code&gt; exits 1 on native Windows. Inspection still works: &lt;code&gt;flisk --version&lt;/code&gt;, &lt;code&gt;flisk doctor&lt;/code&gt;, &lt;code&gt;flisk up --dry-run&lt;/code&gt;. Startup needs Ubuntu on WSL2. Compose does not have that cliff. If the team will not install WSL2, the evaluation is over.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the cluster is a costume
&lt;/h2&gt;

&lt;p&gt;The comparison doc's overhead claim should be quoted as a claim, not as a bench this repository measured. It says a local Kubernetes control plane typically consumes 4 GiB to 8 GiB before your application starts. Use that as a reason to measure your own &lt;code&gt;minikube start&lt;/code&gt;, not as a number to put in a slide without the word "typically."&lt;/p&gt;

&lt;p&gt;The costs that do not need a RAM chart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You maintain Deployments, Services, and Ingress objects so a developer can hit a route.&lt;/li&gt;
&lt;li&gt;On kind, a locally built image has to be loaded into the node. That load is part of the loop, not a footnote.&lt;/li&gt;
&lt;li&gt;Ingress wants a controller, and sometimes MetalLB or &lt;code&gt;minikube tunnel&lt;/code&gt;, before &lt;code&gt;*.localhost&lt;/code&gt; has an equivalent.&lt;/li&gt;
&lt;li&gt;A remote dev cluster adds network delay, someone else's namespace policy, and a cloud bill to an edit that did not need any of them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flisk's counter-offer is plain Docker, Tilt live update into the running container, Traefik on &lt;code&gt;*.localhost&lt;/code&gt;, and &lt;code&gt;flisk up &amp;lt;stack&amp;gt;&lt;/code&gt; instead of a namespace selector. Production stays on Helm, Kustomize, or Argo CD. The chart remains the thing you lint before merge. It stops being the thing you boot to change a button.&lt;/p&gt;

&lt;p&gt;Plain Tilt is the other honest alternative. If the team already writes Starlark, deploys with &lt;code&gt;k8s_yaml()&lt;/code&gt;, or runs host processes beside containers, Flisk is an abstraction over a file they meant to own. &lt;code&gt;flisk eject&lt;/code&gt; points at the generated file if you want that ownership later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tilt up &lt;span class="nt"&gt;-f&lt;/span&gt; .tdk/.tdk-out/Tiltfile &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--focus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;stack&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The migration that does not throw the chart away
&lt;/h2&gt;

&lt;p&gt;From kind or Skaffold to Flisk, keep the production manifests. Add &lt;code&gt;service.json&lt;/code&gt; for the inner loop. Stop requiring every laptop to run &lt;code&gt;kind create cluster&lt;/code&gt; before a UI change. Charts still install in CI. That is the correct place to discover that a template is wrong.&lt;/p&gt;

&lt;p&gt;From Flisk back to a cluster, the work is the work you skipped: start the cluster, install the ingress or the operators, write the chart, deploy, forward a port. There is no command that turns a stack into a kind cluster. If that missing command is what you wanted, you wanted Skaffold.&lt;/p&gt;

&lt;p&gt;Bring-your-own is the middle. A service that already has an image stays on that image. Flisk routes and health-checks it. The image your pipeline ships can be the image the laptop runs, which is the one parity that is worth paying for. Cluster API parity is a different bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  A one-question filter
&lt;/h2&gt;

&lt;p&gt;Ask what the next edit needs to be true.&lt;/p&gt;

&lt;p&gt;If the edit needs a ServiceAccount, a CRD, or a NetworkPolicy to fail realistically, start the cluster and do not apologize. If the edit needs a handler, a query, a prompt, or a pixel, a control plane is time you will not get back. Boot the containers. Leave the API server for the pull request that is about the API server.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>service.json Is Not values.yaml</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:02:32 +0000</pubDate>
      <link>https://dev.to/flisk/servicejson-is-not-valuesyaml-534c</link>
      <guid>https://dev.to/flisk/servicejson-is-not-valuesyaml-534c</guid>
      <description>&lt;p&gt;The sentence that keeps getting shortened is this one: Flisk and Helm are not alternatives.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tdk up&lt;/code&gt; starts Docker containers on your machine through Tilt. &lt;code&gt;helm upgrade&lt;/code&gt; creates Deployments and Services in a cluster. &lt;code&gt;service.json&lt;/code&gt; describes the local service. &lt;code&gt;values.yaml&lt;/code&gt; configures Kubernetes objects. The files share a few words. They are not a translation.&lt;/p&gt;

&lt;p&gt;The project's Helm note says there is no export command. &lt;code&gt;tdk config verify&lt;/code&gt; checks generated project files against &lt;code&gt;.tdk/project.json&lt;/code&gt;. It does not lint &lt;code&gt;service.json&lt;/code&gt;, and it does not lint Helm values. &lt;code&gt;tdk doctor&lt;/code&gt; checks whether this machine can run the local stack. A green doctor is not a green chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The words that look like a mapping
&lt;/h2&gt;

&lt;p&gt;This table is the conceptual one from the Helm doc. Read the last column louder than the first.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You wrote this locally&lt;/th&gt;
&lt;th&gt;Someone will look for this in the chart&lt;/th&gt;
&lt;th&gt;Why the copy fails&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;appName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Release name or controller id&lt;/td&gt;
&lt;td&gt;Identity only. Chart naming is configurable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;appType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment, Job, or CronJob&lt;/td&gt;
&lt;td&gt;A local type does not select the cluster controller.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;port&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;service.main.ports.http.port&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Confirm the container port and the Service port for the image you actually published.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;healthCheckPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Readiness or liveness path&lt;/td&gt;
&lt;td&gt;Add the probes. Tune them. A local curl check is not a probe spec.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dependsOn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hooks, init containers, ordering&lt;/td&gt;
&lt;td&gt;Local startup order is not a cluster startup contract.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traefik on &lt;code&gt;*.localhost&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ingress or Gateway host&lt;/td&gt;
&lt;td&gt;Class, DNS, and TLS are yours.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local Postgres&lt;/td&gt;
&lt;td&gt;External database or a chart dependency&lt;/td&gt;
&lt;td&gt;Lifecycle, persistence, credentials, backups.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A worker is the cleanest example. Locally it is a process in a container. In the cluster it is a Job or a CronJob, and a CronJob needs a schedule. Flisk cannot infer either. A frontend that nginx and Traefik served on your laptop still needs an Ingress or a Gateway that you configure. The local route disappearing from the laptop does not create that object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not copy the generated tree
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.autogenerated/&lt;/code&gt; is local Docker and Tilt output. &lt;code&gt;.tdk/.tdk-out/&lt;/code&gt; is the same family of output. Neither is a chart. The failure mode is a pull request that adds the generated Dockerfile, the generated nginx config, or the generated Tiltfile under &lt;code&gt;charts/&lt;/code&gt; because an agent noticed the files existed and assumed they were the deployable form.&lt;/p&gt;

&lt;p&gt;They are not. The local image is built from golden layers that exist only on the machine that built them. Flisk does not publish that image. Before Helm can run anything, your existing CI has to build and push an image to a registry the cluster can pull. The tag in &lt;code&gt;values.yaml&lt;/code&gt; is that image. It is not &lt;code&gt;shop_orders-api:dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The handwritten example in the repo keeps bjw-s-labs app-template for production and pins &lt;code&gt;oci://ghcr.io/bjw-s-labs/helm/app-template&lt;/code&gt; at &lt;code&gt;5.2.1&lt;/code&gt;. It shows an image repository, a tag, and an HTTP port. It intentionally leaves replicas, probes, ingress, PVCs, resources, security context, identity, and autoscaling to the person who owns the release. The example is a handoff sketch, not a supported production chart. The doc tells you to pull that pinned chart and lint the sample without installing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm pull oci://ghcr.io/bjw-s-labs/helm/app-template &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 5.2.1 &lt;span class="nt"&gt;--untar&lt;/span&gt; &lt;span class="nt"&gt;--untardir&lt;/span&gt; /tmp/tdk-helm-example
helm lint /tmp/tdk-helm-example/app-template &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--values&lt;/span&gt; examples/one-backend-helm/values.yaml
helm template api /tmp/tdk-helm-example/app-template &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--values&lt;/span&gt; examples/one-backend-helm/values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole integration. Keep the chart you already use. Flisk does not install it and does not take it over.&lt;/p&gt;

&lt;h2&gt;
  
  
  The review that actually catches the drift
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tdk up shop&lt;/code&gt; can be healthy while the chart is a lie, because nothing in the local loop reads the chart. Put the check in the pull request instead.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The image repository and tag in values point at an image CI published. Not at a local golden-layer tag.&lt;/li&gt;
&lt;li&gt;The container port in values was confirmed against the image, not copied from &lt;code&gt;service.json&lt;/code&gt; because the numbers matched last month.&lt;/li&gt;
&lt;li&gt;Probes were added on purpose. &lt;code&gt;healthCheckPath&lt;/code&gt; was an input to that decision, not the probe.&lt;/li&gt;
&lt;li&gt;A worker has an explicit Job or CronJob, and a CronJob has a schedule.&lt;/li&gt;
&lt;li&gt;A frontend has an Ingress or Gateway that is not &lt;code&gt;*.localhost&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No file under &lt;code&gt;.autogenerated/&lt;/code&gt; or &lt;code&gt;.tdk/.tdk-out/&lt;/code&gt; was copied into the chart.&lt;/li&gt;
&lt;li&gt;Secrets, IRSA, HPA, node selectors, and PVCs are still owned by the deployment, because the local manifest has nowhere to put them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip Flisk entirely when &lt;code&gt;helm install&lt;/code&gt;, Compose, or a Tiltfile you already like is the local environment. Adding &lt;code&gt;service.json&lt;/code&gt; beside a working chart does not make the chart more true. It adds a second file that can drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  The size of the handoff
&lt;/h2&gt;

&lt;p&gt;Flisk's job ends at the laptop. Helm's job starts at the registry. The dangerous area is the inch between them, where a port, a path, and an image name look portable.&lt;/p&gt;

&lt;p&gt;Carry those three across by hand. Then lint the chart. The CLI that started the container is not going to do it for you.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>helm</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>local-jwt Is Not an Identity Provider</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 23:00:42 +0000</pubDate>
      <link>https://dev.to/flisk/local-jwt-is-not-an-identity-provider-4g9d</link>
      <guid>https://dev.to/flisk/local-jwt-is-not-an-identity-provider-4g9d</guid>
      <description>&lt;p&gt;Flisk is kind to a new service. That kindness is the bug.&lt;/p&gt;

&lt;p&gt;A scaffolded backend, read from the files &lt;code&gt;flisk project&lt;/code&gt; and &lt;code&gt;flisk resource&lt;/code&gt; generated in Flisk 1.3.86, boots with a working login, a working database, a working URL, and a migration that already ran. A real application and a staging environment were not part of that reading. If your code treats those conveniences as the world, it will pass every local test and fail the first time it meets an identity provider.&lt;/p&gt;

&lt;p&gt;The scope note says this plainly. Local is the intended use. CI can be a throwaway copy of local. Staging and production are out of scope. There is no command that deploys, exports a chart, or writes cluster config.&lt;/p&gt;

&lt;h2&gt;
  
  
  The harness, line by line
&lt;/h2&gt;

&lt;p&gt;These are not opinions. They were read from the generated Compose file and &lt;code&gt;.env&lt;/code&gt; of that scaffolded backend. Check them against your own tree before you quote them in a design doc.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What your app sees&lt;/th&gt;
&lt;th&gt;What Flisk actually gave it&lt;/th&gt;
&lt;th&gt;What production will not give it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NODE_ENV=development&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set in the container. &lt;code&gt;TDK_ENV&lt;/code&gt; comes from &lt;code&gt;TILT_ENV&lt;/code&gt;, default &lt;code&gt;development&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Your flags, including a real &lt;code&gt;production&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A JWT that verifies&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AUTH_MODE=local-jwt&lt;/code&gt;, signed with a &lt;code&gt;JWT_SECRET&lt;/code&gt; generated per project in &lt;code&gt;.env&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Your identity provider and key management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postgres on a name you can guess&lt;/td&gt;
&lt;td&gt;One shared Postgres 16 container. One database per stack, named &lt;code&gt;&amp;lt;project&amp;gt;_&amp;lt;stack&amp;gt;&lt;/code&gt;. Password from &lt;code&gt;.env&lt;/code&gt;. Data in a Docker volume&lt;/td&gt;
&lt;td&gt;A managed database, backups, and a migrator that runs once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tables already exist&lt;/td&gt;
&lt;td&gt;Services may migrate at container start. &lt;code&gt;AUTO_MIGRATE&lt;/code&gt; is the escape hatch. &lt;code&gt;flisk doctor&lt;/code&gt; warns about it&lt;/td&gt;
&lt;td&gt;A one-shot job, not "every replica migrates on boot"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http://api.shop.localhost/...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Traefik on &lt;code&gt;*.localhost&lt;/code&gt;, host ports 80 and 443 when free, otherwise a port from 8080–8180 and 8443–8543&lt;/td&gt;
&lt;td&gt;Your ingress, DNS, and certificates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets that are just there&lt;/td&gt;
&lt;td&gt;Git-ignored &lt;code&gt;.env&lt;/code&gt;, or Infisical if &lt;code&gt;TDK_SECRET_PROVIDER=infisical&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Your secret store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;It fits&lt;/td&gt;
&lt;td&gt;Scaffolded backend limits: 512 MB and 0.5 CPU, with 256 MB and 0.2 CPU reserved&lt;/td&gt;
&lt;td&gt;Replicas, real limits, autoscaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unhealthy means curl failed&lt;/td&gt;
&lt;td&gt;Generated services get a Compose health check that runs &lt;code&gt;curl&lt;/code&gt; inside the image. A bring-your-own service gets no Compose health check. Traefik's own check still applies. That was read in &lt;code&gt;compose.star&lt;/code&gt;, not executed&lt;/td&gt;
&lt;td&gt;Your readiness and liveness probes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;HTTPS is the line to be careful with. Traefik has an HTTPS entry point. How certificates are handled was not checked. Do not write "Flisk does local TLS" in a security review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three strings that must never land in source
&lt;/h2&gt;

&lt;p&gt;The failure is not that Flisk uses them. The failure is an agent, or a tired human, pasting them into the application because the request succeeded.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;*.localhost&lt;/code&gt; is a route, not a domain your frontend may hardcode. &lt;code&gt;tdk up&lt;/code&gt; prints the URL. On Linux and WSL2 a non-root user cannot check port 80, so the fallback range is used and the printed URL includes a port. An agent that writes &lt;code&gt;http://api.shop.localhost/api/orders&lt;/code&gt; into a client has coupled the product to one machine's port luck. A resource named &lt;code&gt;orders-api&lt;/code&gt; is served at &lt;code&gt;/api/orders&lt;/code&gt; because a trailing &lt;code&gt;-api&lt;/code&gt; is dropped. That rule is also a Flisk rule. Read the base URL from the environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;project&amp;gt;_&amp;lt;stack&amp;gt;&lt;/code&gt; is a local database name. The next stack on that same Postgres container gets its own database, which feels like isolation and is not a production topology. One volume holds the data. &lt;code&gt;flisk down&lt;/code&gt; keeps the project networks unless you pass &lt;code&gt;--prune-networks&lt;/code&gt;. So "I reset the stack" often means "I stopped containers and left the network sitting there." If your seed assumes a fresh network or a fresh volume, it will pass on a new laptop and fail on the laptop that has been up since Tuesday.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;local-jwt&lt;/code&gt; plus a project-local &lt;code&gt;JWT_SECRET&lt;/code&gt; is a dev login. It is not a tenant model, not a rotation story, and not something another service should accept outside this Compose file. The alternative Flisk offers is an identity service when you add one. Add one before you demo auth to anyone who will ask where the keys live.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AUTO_MIGRATE&lt;/code&gt; on boot is the same shape of bug. It makes the first hour painless and makes "two replicas started together" a coin flip. Doctor warns. The warning is not a migration runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI is allowed to be this fake. Staging is not.
&lt;/h2&gt;

&lt;p&gt;The repository's own quickstart workflow runs a real &lt;code&gt;flisk up&lt;/code&gt; on a GitHub-hosted runner. That is a throwaway environment, which is a fair use. A copy-paste workflow for your own repository lives in the CI docs, and its &lt;code&gt;flisk up&lt;/code&gt; job had not been run on GitHub when the scope page was written. Do not treat an unread recipe as evidence that your pipeline already does this.&lt;/p&gt;

&lt;p&gt;A green CI job against &lt;code&gt;NODE_ENV=development&lt;/code&gt; and local JWT does not answer the production question. The scope note's own remedy is small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the image your CI built, through a bring-your-own resource, at least once.&lt;/li&gt;
&lt;li&gt;Read hosts, database names, and auth settings from the environment. Do not branch on "we are on &lt;code&gt;*.localhost&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;Test any &lt;code&gt;NODE_ENV&lt;/code&gt;-dependent behavior in a run that is not Flisk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bring-your-own narrows the gap. It does not make the laptop a cluster. Flisk still will not generate the Helm values. The port, the image, and the health path will exist in &lt;code&gt;service.json&lt;/code&gt; and again in the chart. Nothing syncs them. Changing one is a review of the other, every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  A review checklist that fits in a pull request
&lt;/h2&gt;

&lt;p&gt;Reject the diff if application code contains any of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;localhost&lt;/code&gt; as a service address, including &lt;code&gt;api.&amp;lt;stack&amp;gt;.localhost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the stack database name, or a password read from a comment instead of the environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AUTH_MODE&lt;/code&gt;, &lt;code&gt;JWT_SECRET&lt;/code&gt;, or a token signed for local JWT&lt;/li&gt;
&lt;li&gt;a migration invoked because the process started, with no separate migrator&lt;/li&gt;
&lt;li&gt;a health check that assumes &lt;code&gt;curl&lt;/code&gt; exists in an image you did not build&lt;/li&gt;
&lt;li&gt;a copy of &lt;code&gt;.autogenerated/&lt;/code&gt; or &lt;code&gt;.tdk/.tdk-out/&lt;/code&gt; pasted into a chart&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then ask one question the laptop cannot answer: what happens when this process starts twice, against a database it is not allowed to create, on a host it has never heard of?&lt;/p&gt;

&lt;p&gt;If the answer is "Flisk handles that," the feature is still a local convenience. Ship the other behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The size of the kindness
&lt;/h2&gt;

&lt;p&gt;Flisk should keep being kind. A new service should get a URL, a database, and a token that verifies. The application does not get to remember how that kindness was implemented.&lt;/p&gt;

&lt;p&gt;Local is a harness. The moment the harness is in source, you no longer have a service. You have a demo that only boots in the room where it was written.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>security</category>
      <category>microservices</category>
    </item>
    <item>
      <title>There Are Two Dockerfiles. Your Agent Should Touch Neither.</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 22:58:18 +0000</pubDate>
      <link>https://dev.to/flisk/there-are-two-dockerfiles-your-agent-should-touch-neither-lge</link>
      <guid>https://dev.to/flisk/there-are-two-dockerfiles-your-agent-should-touch-neither-lge</guid>
      <description>&lt;p&gt;A coding agent will edit a Dockerfile the way a helpful intern edits a wiki: confidently, locally, and with no memory of the last time someone did the same thing three services over.&lt;/p&gt;

&lt;p&gt;That is fine in a three-service repo. It is a slow accident in a landscape. &lt;a href="https://flisk.sh/" rel="noopener noreferrer"&gt;Flisk&lt;/a&gt; makes the accident specific, because it already has two Dockerfiles, and &lt;code&gt;flisk up&lt;/code&gt; builds only one of them.&lt;/p&gt;

&lt;p&gt;If your agent cannot tell those two files apart, it should not be allowed to touch either.&lt;/p&gt;

&lt;h2&gt;
  
  
  The file that runs is not the file that looks like source
&lt;/h2&gt;

&lt;p&gt;Read this from the project's own scope note, not from a launch tweet. For a generated service, &lt;code&gt;flisk up&lt;/code&gt; builds &lt;code&gt;services/&amp;lt;stack&amp;gt;/&amp;lt;name&amp;gt;/.autogenerated/Dockerfile.app.autogenerated&lt;/code&gt;. The build context is the project root. The stages start from local golden images, named like &lt;code&gt;&amp;lt;project&amp;gt;-l1:latest&lt;/code&gt;, &lt;code&gt;-l2&lt;/code&gt;, &lt;code&gt;-l3-backend&lt;/code&gt;, &lt;code&gt;-l4-backend&lt;/code&gt;. Those images are built on your machine. They are not published.&lt;/p&gt;

&lt;p&gt;So that Dockerfile cannot be built on a laptop, or in a CI job, that has not built the golden layers first. The final stage runs as user &lt;code&gt;bun&lt;/code&gt;. The local tag looks like &lt;code&gt;shop_orders-api:dev&lt;/code&gt;. The &lt;code&gt;production&lt;/code&gt; and &lt;code&gt;development&lt;/code&gt; targets in that file are the same stage. It is a local image.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flisk resource&lt;/code&gt; also writes a plain &lt;code&gt;Dockerfile&lt;/code&gt; next to &lt;code&gt;service.json&lt;/code&gt;. It even has a comment saying so. &lt;code&gt;flisk up&lt;/code&gt; does not build it. Its &lt;code&gt;PORT&lt;/code&gt;, &lt;code&gt;EXPOSE&lt;/code&gt;, and health check follow the &lt;code&gt;port&lt;/code&gt; in &lt;code&gt;service.json&lt;/code&gt;. It used to say 3000 while a backend ran on 4000, which is exactly the class of lie an agent will reintroduce if you let it "clean up" the file. The plain Dockerfile is a starting point for building outside Flisk. Whether it is fit to ship was not checked. Do not promote it to a production image because it sits in the nice part of the tree.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Who may edit it&lt;/th&gt;
&lt;th&gt;What actually runs it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;service.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You, or the agent, if the task is a topology change&lt;/td&gt;
&lt;td&gt;Nothing. It is the input.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.autogenerated/Dockerfile.app.autogenerated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nobody by hand&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;flisk up&lt;/code&gt;, after local golden layers exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plain &lt;code&gt;Dockerfile&lt;/code&gt; beside &lt;code&gt;service.json&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Nobody, unless you are leaving Flisk's local loop&lt;/td&gt;
&lt;td&gt;Not &lt;code&gt;flisk up&lt;/code&gt;. A later, separate build, if you choose one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your own Dockerfile on a bring-your-own service&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;td&gt;Your image, if you pointed the resource at it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bring-your-own is the escape hatch, not a magic sync. If you want the image you ship to be the image that runs locally, use a bring-your-own resource with your own &lt;code&gt;Dockerfile&lt;/code&gt; or &lt;code&gt;--image&lt;/code&gt;. That shrinks the gap. It does not make local and production the same computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agent is good at, and what it will counterfeit
&lt;/h2&gt;

&lt;p&gt;The agent is good at the thing inside the container: a handler, a query, a failing test, a prompt, a tool call.&lt;/p&gt;

&lt;p&gt;It is bad at the thing around the container, because that thing has to be the same on every service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the port in the manifest, the route, and the health check&lt;/li&gt;
&lt;li&gt;the host npmrc versus the container npmrc&lt;/li&gt;
&lt;li&gt;the Vite config the dev server uses versus the one nginx serves&lt;/li&gt;
&lt;li&gt;the golden layer the local Dockerfile expects&lt;/li&gt;
&lt;li&gt;the Helm value that production actually reads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flisk does not generate a chart. &lt;code&gt;.tdk/.tdk-out/&lt;/code&gt; and &lt;code&gt;.autogenerated/&lt;/code&gt; are local output. Copying them into Helm is how "works on my laptop" becomes a deploy. The port, the image, and the health path will exist twice, once in &lt;code&gt;service.json&lt;/code&gt; and once in the chart. Nothing keeps them in sync. An agent that "helps" by updating only the file it can see will desync them in one commit.&lt;/p&gt;

&lt;p&gt;A generated frontend makes the surface obvious. Under &lt;code&gt;.autogenerated/&lt;/code&gt; you get the local Dockerfile, an nginx config, host and container TypeScript configs, host and container package configs, Vite configs, an API client, and a short &lt;code&gt;AGENTS.md&lt;/code&gt;. A Bun backend swaps in backend env, Vite, and Prisma files. Python, Go, and Rust still get a local Dockerfile, and skip the Bun-only pile. The exact set depends on &lt;code&gt;appType&lt;/code&gt;, language, and features. The rule does not.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;service.json&lt;/code&gt;. Let the generator write the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The warning file is a sign, not a policy
&lt;/h2&gt;

&lt;p&gt;Each service's &lt;code&gt;.autogenerated/AGENTS.md&lt;/code&gt; tells a coding agent that the directory is generated. On the free in-repo generator, that file is a short notice. It is not a substitute for a root policy. The downloaded agents-md implementation is the one their feature table marks as Premium. Do not assume a two-paragraph warning will stop a determined agent that was told to "make the container build."&lt;/p&gt;

&lt;p&gt;Put the real rule where every task starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# AGENTS.md&lt;/span&gt;

&lt;span class="gu"&gt;## Source of truth&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Topology changes go in &lt;span class="sb"&gt;`service.json`&lt;/span&gt; or &lt;span class="sb"&gt;`.tdk/project.json`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Application code stays in the service, outside &lt;span class="sb"&gt;`.autogenerated/`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Production deploy config is Helm or the pipeline. Flisk does not generate it.

&lt;span class="gu"&gt;## Do not edit&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`.autogenerated/**`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`.tdk/.tdk-out/**`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`.autogenerated/.tdk-contract/**`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; The plain &lt;span class="sb"&gt;`Dockerfile`&lt;/span&gt; next to &lt;span class="sb"&gt;`service.json`&lt;/span&gt;, unless the task is explicitly "prepare a non-Flisk build" and a human asked for that file.

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Inspect: &lt;span class="sb"&gt;`flisk doctor`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Preview: &lt;span class="sb"&gt;`flisk up &amp;lt;stack&amp;gt; --dry-run`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Run: &lt;span class="sb"&gt;`flisk up &amp;lt;stack&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Check the generated contract: &lt;span class="sb"&gt;`flisk config verify`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; After a manifest change, run &lt;span class="sb"&gt;`flisk up`&lt;/span&gt; again. Do not patch generated output to match the manifest.

&lt;span class="gu"&gt;## Stop and ask&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; The task needs a production image, a cluster, or a secret from &lt;span class="sb"&gt;`.env`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; A generated file looks wrong. Change the manifest or the generator, then regenerate.
&lt;span class="p"&gt;-&lt;/span&gt; Local auth, &lt;span class="sb"&gt;`*.localhost`&lt;/span&gt;, or the shared Postgres database name would have to be hardcoded. Read those from the environment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;tdk&lt;/code&gt; in that file if that is the binary your install prints. The package and the docs still disagree on the name. The boundary does not.&lt;/p&gt;

&lt;p&gt;This file is guidance, not a sandbox. An agent can ignore it. The drift check is the part that does not care about the agent's manners.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate is more useful than the lecture
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;flisk up&lt;/code&gt; compares generated service files with the snapshot in &lt;code&gt;.autogenerated/.tdk-contract/&lt;/code&gt; before Tilt starts. A hand edit blocks startup and names the file. &lt;code&gt;--dry-run&lt;/code&gt; runs the same check and starts nothing. &lt;code&gt;service.yaml&lt;/code&gt; is exempt from that comparison. It is a projection of &lt;code&gt;service.json&lt;/code&gt; for Tilt. Edit the JSON.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flisk config verify&lt;/code&gt; checks the wider contract under &lt;code&gt;.tdk/.tdk-out/&lt;/code&gt;, validates discovered manifests, and compares service files with their snapshots. &lt;code&gt;flisk doctor&lt;/code&gt; is for machine readiness, not for "did an agent rewrite nginx."&lt;/p&gt;

&lt;p&gt;The repair is boring, which is the point. Move the intended change into &lt;code&gt;service.json&lt;/code&gt;, then run &lt;code&gt;flisk up&lt;/code&gt; so the outputs are regenerated. &lt;code&gt;flisk config regenerate&lt;/code&gt; replaces generated project files. A hand-edited service file keeps blocking until the edit is gone or the check is explicitly bypassed. Bypass is a human decision. It is not a step in an agent prompt.&lt;/p&gt;

&lt;p&gt;The earlier post on derived artifacts covers that gate. This post is about the file the agent can see and still misunderstand.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the agent should edit a Dockerfile
&lt;/h2&gt;

&lt;p&gt;Sometimes the Dockerfile is the source. Say so, or the rule becomes superstition.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two or three services and a Compose file that already fits on one screen. Flisk is extra machinery. Let the agent edit Compose and the Dockerfile, then read the diff yourself.&lt;/li&gt;
&lt;li&gt;A bring-your-own service whose image is built by your CI. The Dockerfile next to that service is product infrastructure. Review it like code. Do not also let the agent "fix" the generated local Dockerfile so they match. They are not supposed to match line for line.&lt;/li&gt;
&lt;li&gt;A one-off debug of the golden-layer build, done by a human who is changing the generator. That edit belongs in the generator, then every service regenerates. It does not belong in one service's &lt;code&gt;.autogenerated/&lt;/code&gt; folder as a snowflake.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The test is ownership. If deleting the file and running &lt;code&gt;flisk up&lt;/code&gt; would recreate it, the agent does not own it. If deleting it would break the image your pipeline ships, the agent may edit it, and a human reviews the diff before merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  One loop that keeps the agent in its lane
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flisk doctor
flisk up shop &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;span class="c"&gt;# agent edits application code, or service.json if the task is topology&lt;/span&gt;
flisk config verify
flisk up shop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If verify fails because the agent touched &lt;code&gt;.autogenerated/&lt;/code&gt;, revert that path. Do not ask the agent to reconcile the generated Dockerfile with the plain Dockerfile. That reconciliation is the bug.&lt;/p&gt;

&lt;p&gt;Local Flisk also leaks fake production if you let application code depend on it. A scaffolded backend runs with &lt;code&gt;NODE_ENV=development&lt;/code&gt;, local JWT auth, one shared Postgres 16, and &lt;code&gt;*.localhost&lt;/code&gt; routes. Those are environment facts. The moment they are string literals in source, the agent has coupled the product to a dev harness. The scope note's rule is the right one: run the real production image through a bring-your-own resource at least once, and keep Flisk-only hosts and database names out of the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The size of this rule
&lt;/h2&gt;

&lt;p&gt;Flisk is still a small CLI. This rule is the large part. A coding agent can write the service. It cannot be the system of record for how that service is built, routed, and health-checked on a laptop, and it definitely cannot be the system of record for the image you ship.&lt;/p&gt;

&lt;p&gt;Give it &lt;code&gt;service.json&lt;/code&gt;, the application tree, and a root &lt;code&gt;AGENTS.md&lt;/code&gt; that names the forbidden directories. Take the Dockerfiles away.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>docker</category>
      <category>devops</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Your Stack Has a Size. Flisk Only Fits One of Them.</title>
      <dc:creator>flisk</dc:creator>
      <pubDate>Wed, 21 Oct 2026 22:47:28 +0000</pubDate>
      <link>https://dev.to/flisk/your-stack-has-a-size-flisk-only-fits-one-of-them-1n1f</link>
      <guid>https://dev.to/flisk/your-stack-has-a-size-flisk-only-fits-one-of-them-1n1f</guid>
      <description>&lt;p&gt;"We have microservices" is not a sizing input. Twelve services in one Compose file is a different job from twelve services that each own a manifest, a route, and a health check. &lt;a href="https://flisk.sh/" rel="noopener noreferrer"&gt;Flisk&lt;/a&gt; is opinionated about which of those jobs it wants.&lt;/p&gt;

&lt;p&gt;This is the decision table I wish every "should we adopt this" thread started with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three sizes, three tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your actual stack&lt;/th&gt;
&lt;th&gt;Better default&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2–5 services, already containerized&lt;/td&gt;
&lt;td&gt;Docker Compose&lt;/td&gt;
&lt;td&gt;One file, no new abstraction, everyone already knows it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You already love your Tiltfile&lt;/td&gt;
&lt;td&gt;Plain Tilt&lt;/td&gt;
&lt;td&gt;Flisk's job is to write that file. If it exists and works, leave it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local must match the cluster&lt;/td&gt;
&lt;td&gt;kind, minikube, Skaffold, DevSpace, Garden&lt;/td&gt;
&lt;td&gt;Those tools target Kubernetes. Flisk does not&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10–50+ services, painful Compose, agents rewriting Dockerfiles&lt;/td&gt;
&lt;td&gt;Flisk&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;service.json&lt;/code&gt; per service, &lt;code&gt;flisk up &amp;lt;stack&amp;gt;&lt;/code&gt;, generated Tilt loop, no local cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You need a platform, SSO, and an SLA tomorrow&lt;/td&gt;
&lt;td&gt;Not this, yet&lt;/td&gt;
&lt;td&gt;Four listed adopters, small community, optional Premium at $19/dev/month. Enterprise is "contact them"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The middle row is the product. Everything else is a way to waste a week proving a README you could have believed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "medium" feels like on a Tuesday
&lt;/h2&gt;

&lt;p&gt;You are not booting an ERP. You are trying to change checkout without starting payroll.&lt;/p&gt;

&lt;p&gt;Flisk's answer is stack-scoped startup. &lt;code&gt;flisk up shop&lt;/code&gt; starts that stack. &lt;code&gt;flisk up shop --dry-run&lt;/code&gt; prints the services and URLs and starts nothing. Ports come from the manifest. Routes land on a local domain such as &lt;code&gt;http://api.shop.localhost/...&lt;/code&gt; when port 80 is free, otherwise on a port the CLI prints. Dev ports bind to &lt;code&gt;127.0.0.1&lt;/code&gt; unless you set &lt;code&gt;TDK_BIND_ADDRESS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is the feature. Not "100 services on a laptop."&lt;/p&gt;

&lt;p&gt;The 100-service figure is a generated &lt;code&gt;/health&lt;/code&gt; fixture: small stub services, all routed through Traefik, used as a boot test. The project says a 14-service stack is the daily driver on a 16 GB M1, and a 27-service monorepo runs on the same engine. Quote those. Do not quote the fixture as capacity planning.&lt;/p&gt;

&lt;p&gt;Memory scales with what the process does. A 20-line health handler at about 17 MiB does not predict a Java API with Hibernate. If your service needs 1 GB, fifty of them do not fit because a stub bench said 1.63 GiB.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent test is stricter than the human test
&lt;/h2&gt;

&lt;p&gt;A human can maintain a messy Compose file through folklore. An agent cannot. It will regenerate the Dockerfile, drift the Tiltfile, and "fix" nginx until the health check is a lie.&lt;/p&gt;

&lt;p&gt;The useful Flisk behavior is drift control. You own &lt;code&gt;service.json&lt;/code&gt;. The CLI owns &lt;code&gt;.autogenerated/&lt;/code&gt;. &lt;code&gt;flisk config verify&lt;/code&gt; tells you when those generated files no longer match what the CLI would write. That is a small command with a large effect: the agent has a spec, and you have a diff when the spec was ignored.&lt;/p&gt;

&lt;p&gt;Scaffolding follows the same idea.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flisk resource bot &lt;span class="nt"&gt;--type&lt;/span&gt; agent &lt;span class="nt"&gt;--stack&lt;/span&gt; shop &lt;span class="nt"&gt;--yes&lt;/span&gt;
flisk resource tools-mcp &lt;span class="nt"&gt;--type&lt;/span&gt; mcp &lt;span class="nt"&gt;--stack&lt;/span&gt; shop &lt;span class="nt"&gt;--yes&lt;/span&gt;
flisk up shop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent framework still owns prompts, tool calls, and retries. Flisk owns the local process, the checkpoint volume, the MCP server route, Postgres, and the gateway. If you do not have that second problem, you do not need this CLI just because you use an agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pilot that can fail in a day
&lt;/h2&gt;

&lt;p&gt;Do not migrate the monorepo. Pick one stack that already hurts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;flisk doctor&lt;/code&gt;. If Docker or Tilt is missing, the tool is not your bottleneck.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;service.json&lt;/code&gt; beside one existing service. Bring-your-own is a documented path. You do not have to accept the Bun/TypeScript skeleton.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flisk up that-stack --dry-run&lt;/code&gt;. Read the URL list with the person who owns the Compose file.&lt;/li&gt;
&lt;li&gt;Start it. Hit &lt;code&gt;/health&lt;/code&gt;. Change a file. Confirm Tilt updates the container instead of you rebuilding by hand.&lt;/li&gt;
&lt;li&gt;Stop if the generated files fight a Dockerfile you must keep byte-for-byte. Gradual adoption exists. A rewrite disguised as a pilot does not.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Windows note, because it wastes the first hour: native Windows can inspect (&lt;code&gt;version&lt;/code&gt;, &lt;code&gt;doctor&lt;/code&gt;, &lt;code&gt;up --dry-run&lt;/code&gt;). Starting the stack needs Ubuntu on WSL2.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you should not put in the adoption slide
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;"Replaces Kubernetes." It does not. Production stays on Helm.&lt;/li&gt;
&lt;li&gt;"Saves five hours a week." Their own site says the calculator is arithmetic on your inputs, and the five-hour starting value is an editable input, not a measurement.&lt;/li&gt;
&lt;li&gt;"Onboarding drops from three days to minutes." Designed for that. Not measured on a hiring cohort.&lt;/li&gt;
&lt;li&gt;"Runs 100 production services on a MacBook." Runs 100 health stubs in a bench. Different sentence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The fit, in one line
&lt;/h2&gt;

&lt;p&gt;Flisk is too big for a Compose file that already fits on one screen, and too small to be your platform team. It fits the stack that has outgrown one file and has not earned a local cluster.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>devops</category>
      <category>docker</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
