<?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: ntoledo319</title>
    <description>The latest articles on DEV Community by ntoledo319 (@ntoledo319).</description>
    <link>https://dev.to/ntoledo319</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%2F3997589%2F5bdd25d8-a708-4db4-8311-e27a6be8b853.png</url>
      <title>DEV Community: ntoledo319</title>
      <link>https://dev.to/ntoledo319</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ntoledo319"/>
    <language>en</language>
    <item>
      <title>"Error: The module was compiled against a different Node.js version using NODE_MODULE_VERSION — fixing native addon failures after a Lambda upgrade"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Mon, 03 Aug 2026 07:17:02 +0000</pubDate>
      <link>https://dev.to/ntoledo319/error-the-module-was-compiled-against-a-different-nodejs-version-using-nodemoduleversion--20ma</link>
      <guid>https://dev.to/ntoledo319/error-the-module-was-compiled-against-a-different-nodejs-version-using-nodemoduleversion--20ma</guid>
      <description>&lt;p&gt;You upgrade a Lambda function from &lt;code&gt;nodejs20.x&lt;/code&gt; to &lt;code&gt;nodejs22.x&lt;/code&gt;, redeploy, and the first cold start dies with something 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;Error: The module '/var/task/node_modules/sharp/build/Release/sharp-linux-x64-115.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 115. This version of Node.js requires
NODE_MODULE_VERSION 127. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbers vary by package (&lt;code&gt;115&lt;/code&gt; and &lt;code&gt;127&lt;/code&gt; are Node 20 and Node 22 respectively), but the shape is always the same: a &lt;code&gt;.node&lt;/code&gt; binary was compiled for one ABI and the runtime is now a different one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;Every Node.js major version assigns a new &lt;strong&gt;NODE_MODULE_VERSION&lt;/strong&gt; — an integer baked into compiled &lt;code&gt;.node&lt;/code&gt; files that the runtime checks at load time:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node.js version&lt;/th&gt;
&lt;th&gt;NODE_MODULE_VERSION&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node.js 20&lt;/td&gt;
&lt;td&gt;115&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js 22&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Native addons — &lt;code&gt;sharp&lt;/code&gt;, &lt;code&gt;bcrypt&lt;/code&gt;, &lt;code&gt;better-sqlite3&lt;/code&gt;, &lt;code&gt;canvas&lt;/code&gt;, &lt;code&gt;grpcio&lt;/code&gt;, and anything else that ships a precompiled &lt;code&gt;.node&lt;/code&gt; file — link against the ABI of the Node version they were built on. When the runtime changes, the loader reads the embedded version, sees it doesn't match the running Node, and refuses to load the binary.&lt;/p&gt;

&lt;p&gt;This is intentional: the ABI mismatch means the binary literally cannot be safely loaded. The error is a hard stop at cold-start time, not a warning.&lt;/p&gt;

&lt;p&gt;The problem surfaces specifically after a Lambda runtime bump because your CI pipeline typically builds the deployment package once (on Node 20, say) and keeps redeploying the same artifact. The binary in &lt;code&gt;/var/task/node_modules/&amp;lt;pkg&amp;gt;/build/Release/*.node&lt;/code&gt; was compiled for Node 20's ABI and the new &lt;code&gt;nodejs22.x&lt;/code&gt; runtime won't touch it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Rebuild the deployment package on the target Node version
&lt;/h3&gt;

&lt;p&gt;The root fix is always: &lt;strong&gt;build where you deploy&lt;/strong&gt;. Tear down &lt;code&gt;node_modules&lt;/code&gt; and reinstall on a machine (or container) running Node 22:&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;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; node_modules
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you can't change the build machine, force a rebuild:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm rebuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then re-bundle and redeploy. Crucially, do this inside the Lambda base image if you have native dependencies, so the binary also links against the right glibc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/var/task &lt;span class="se"&gt;\&lt;/span&gt;
  public.ecr.aws/lambda/nodejs:22 &lt;span class="se"&gt;\&lt;/span&gt;
  npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a binary that is both the right ABI &lt;em&gt;and&lt;/em&gt; linked against the right glibc — avoiding a second failure down the line.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. If the package has no Node 22 prebuilt, upgrade it
&lt;/h3&gt;

&lt;p&gt;Some packages ship prebuilt binaries keyed to specific Node major versions. If your pinned version has no Node 22 prebuild, the install falls back to compiling from source — which can fail in a CI environment without the right build toolchain. The fix is to upgrade to a version that does ship Node 22 prebuilts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sharp&lt;/strong&gt;: upgrade to &lt;code&gt;&amp;gt;= 0.33.0&lt;/code&gt; (moved to Node-API, no longer tied to a specific ABI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bcrypt&lt;/strong&gt;: &lt;code&gt;&amp;gt;= 5.1.1&lt;/code&gt; has Node 22 prebuilt support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;better-sqlite3&lt;/strong&gt;: &lt;code&gt;&amp;gt;= 11.0.0&lt;/code&gt; for Node 22; consider &lt;code&gt;&amp;gt;= 13.0.0&lt;/code&gt; which switched to N-API for future-proof ABI compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. For dead packages, replace them — don't rebuild them
&lt;/h3&gt;

&lt;p&gt;Some native packages have no Node 22 build and no intention of shipping one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dead package&lt;/th&gt;
&lt;th&gt;Replacement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node-sass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sass&lt;/code&gt; (Dart Sass — pure JS, no native build needed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fibers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove it; Node's async primitives have superseded it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grpc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@grpc/grpc-js&lt;/code&gt; (pure JS gRPC client)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These replacements are also smaller and faster in Lambda cold-start terms because they have no native compilation step.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Lock your build Node version in CI
&lt;/h3&gt;

&lt;p&gt;Add an explicit engine constraint so a future CI runner upgrade can't silently produce an incompatible artifact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"22.x"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or pin the Node version in your CI environment file (&lt;code&gt;.nvmrc&lt;/code&gt;, &lt;code&gt;.node-version&lt;/code&gt;, GitHub Actions &lt;code&gt;node-version: '22'&lt;/code&gt;). The goal: whatever Node version runs &lt;code&gt;npm install&lt;/code&gt; in CI must match the Lambda runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catching it before the cold start
&lt;/h2&gt;

&lt;p&gt;The mismatch is silent until a cold start fires in production. The fastest way to surface it earlier: &lt;strong&gt;import the native module in a smoke test that runs inside the Lambda base image&lt;/strong&gt; (&lt;code&gt;public.ecr.aws/lambda/nodejs:22&lt;/code&gt;), not just on your developer machine.&lt;/p&gt;




&lt;p&gt;If you'd rather get a full picture of which Lambda functions carry native-addon ABI risk — alongside deprecated-runtime exposure — without auditing each deployment package by hand, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks an account in about 30 seconds, nothing uploaded. Full reference: &lt;strong&gt;&lt;a href="https://eolkits.com/fix/node-module-version-mismatch/" rel="noopener noreferrer"&gt;eolkits.com/fix/node-module-version-mismatch&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>"Why did my AWS deploy break with zero code changes?"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Sat, 01 Aug 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/why-did-my-aws-deploy-break-with-zero-code-changes-5dg9</link>
      <guid>https://dev.to/ntoledo319/why-did-my-aws-deploy-break-with-zero-code-changes-5dg9</guid>
      <description>&lt;p&gt;Every other class of "it broke" starts with a diff. This one doesn't. &lt;code&gt;git log&lt;/code&gt; shows nothing since last week, CI was green then, it's red now, and the error doesn't look like anything your team wrote.&lt;/p&gt;

&lt;p&gt;That's the tell. A deploy that breaks with zero code changes is almost always one of two things: a fixed AWS calendar cutoff you crossed passively, or a dependency/base-image that moved out from under you without a corresponding commit in your repo. Neither shows up in &lt;code&gt;git blame&lt;/code&gt;. Here's how to tell them apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: is it a fixed AWS date?
&lt;/h2&gt;

&lt;p&gt;Two calendar cutoffs cover almost every "nothing changed but it broke" report on Lambda and EC2/ECS/EKS right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lambda runtime blocks.&lt;/strong&gt; AWS enforces these on the account, not on your deploy history — the API call itself gets rejected once the date passes, independent of when you last touched the function. As of today, &lt;code&gt;nodejs16.x&lt;/code&gt;, &lt;code&gt;nodejs18.x&lt;/code&gt;, &lt;code&gt;nodejs20.x&lt;/code&gt;, &lt;code&gt;python3.8&lt;/code&gt;, &lt;code&gt;python3.9&lt;/code&gt;, and &lt;code&gt;python3.10&lt;/code&gt; all share the same two dates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Block function &lt;em&gt;create&lt;/em&gt;: Feb 1, 2027&lt;/strong&gt; — &lt;code&gt;CreateFunction&lt;/code&gt; (or a Terraform/CDK/SAM deploy that provisions a &lt;em&gt;new&lt;/em&gt; function or alias) on one of these runtimes is rejected outright after this date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block function &lt;em&gt;update&lt;/em&gt;: Mar 3, 2027&lt;/strong&gt; — after this, &lt;code&gt;UpdateFunctionCode&lt;/code&gt;/&lt;code&gt;UpdateFunctionConfiguration&lt;/code&gt; on an &lt;em&gt;existing&lt;/em&gt; function using one of these runtimes is rejected too. The function keeps running and keeps invoking; you just can't ship a change to it anymore.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(&lt;code&gt;python3.11&lt;/code&gt; is on a later cluster: block-create Jul 31 2027, block-update Aug 31 2027 — don't assume every runtime lines up with the Q1-2027 dates above.)&lt;/p&gt;

&lt;p&gt;If your error is coming back from the Lambda control plane itself — not from inside your code, not from a cold start — rather than a runtime crash, this is almost certainly it. Check the runtime string on the function the deploy touched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Linux 2 end of life.&lt;/strong&gt; AL2 reached end of standard support &lt;strong&gt;June 30, 2026&lt;/strong&gt; — already past. This one doesn't reject an API call the way the Lambda blocks do; instead, AL2 stops getting security patches, and separately, any user-data or AMI-bake step that calls &lt;code&gt;yum&lt;/code&gt;/&lt;code&gt;amazon-linux-extras&lt;/code&gt; starts failing as AL2's package repos and Extras Library shift under it. If the failure is at &lt;em&gt;instance boot&lt;/em&gt; or &lt;em&gt;AMI bake&lt;/em&gt; time rather than at application runtime, check the base AMI before anything else.&lt;/p&gt;

&lt;p&gt;Cross-check either of these in 10 seconds against the same table AWS enforces — paste your runtimes or click the ones you use in the free &lt;strong&gt;&lt;a href="https://eolkits.com/eol-checker/" rel="noopener noreferrer"&gt;EOL checker&lt;/a&gt;&lt;/strong&gt; (client-side, nothing uploaded); it flags exactly which cutoff applies and how many days you have left.&lt;/p&gt;

&lt;h2&gt;
  
  
  If it's not a fixed date: three things that move without a commit
&lt;/h2&gt;

&lt;p&gt;Not every silent break is calendar-driven. These three change underneath you with no corresponding line in your diff:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An unpinned or &lt;code&gt;:latest&lt;/code&gt;-tagged base image moved forward.&lt;/strong&gt; A Docker base image reference that isn't pinned to a digest can resolve to a newer patch build on a fresh pull — bringing a newer OS or language patch version than what last built successfully, with nothing in your Dockerfile diff to show for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A transitive dependency resolved differently.&lt;/strong&gt; A fresh &lt;code&gt;npm ci&lt;/code&gt; or &lt;code&gt;pip install&lt;/code&gt; in CI re-resolves anything not exactly pinned. A transitive package (not your direct requirement) picking up a new minor version can drop or rename an API your code calls indirectly — again, invisible in your own diff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An IaC provider bumped its own defaults.&lt;/strong&gt; A routine &lt;code&gt;terraform init -upgrade&lt;/code&gt; or CDK library bump can change what a resource defaults to when you didn't explicitly set it — including, on Lambda resources, which runtime a construct assumes if a version pin was left implicit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these are bugs in the classic sense — they're all "something you depend on changed, and your repo correctly recorded that you didn't ask it to stay still."&lt;/p&gt;

&lt;h2&gt;
  
  
  Narrowing it down
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read where the error actually comes from.&lt;/strong&gt; A rejection with no invocation ever happening, straight from the deploy/API call, points at a Lambda block date. A failure during instance boot or AMI bake points at AL2. A runtime crash after a successful cold start points at a stdlib/dependency change — see the &lt;a href="https://eolkits.com/fix/" rel="noopener noreferrer"&gt;runtime-upgrade error map&lt;/a&gt; for the exact error text against the specific fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check what's actually pinned.&lt;/strong&gt; &lt;code&gt;git diff&lt;/code&gt; won't show you a moving base image tag or an unpinned transitive dependency — &lt;code&gt;docker inspect&lt;/code&gt; on the built image, or a fresh &lt;code&gt;npm ls&lt;/code&gt;/&lt;code&gt;pip freeze&lt;/code&gt; diffed against your last known-good build, will.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you're not sure which functions or AMIs are even exposed&lt;/strong&gt;, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;scanner&lt;/a&gt;&lt;/strong&gt; checks a whole account in about 30 seconds and flags both the calendar-cutoff risk and known dependency landmines before the next deploy trips one — nothing uploaded. I maintain it, disclosing that plainly since it's the one link here that isn't a diagnostic tool or a fix page.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>devops</category>
      <category>serverless</category>
    </item>
    <item>
      <title>"The AWS runtime-upgrade error map: which errors you'll hit, in what order, for each migration path"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Fri, 31 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/the-aws-runtime-upgrade-error-map-which-errors-youll-hit-in-what-order-for-each-migration-path-55d5</link>
      <guid>https://dev.to/ntoledo319/the-aws-runtime-upgrade-error-map-which-errors-youll-hit-in-what-order-for-each-migration-path-55d5</guid>
      <description>&lt;p&gt;Every deprecated-runtime migration produces the same shape of pain: you bump one version number, redeploy, and get hit with errors that look unrelated to each other but are all downstream of the same jump. The errors aren't random — for a given upgrade path, they show up in a fairly predictable order.&lt;/p&gt;

&lt;p&gt;This is a map, not a new deep dive. Each line below is a real, verbatim error with its own fix already written up — this just sequences them by &lt;em&gt;which upgrade produces them&lt;/em&gt;, so you know what's coming before you hit it instead of debugging one surprise at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Path 1: Python 3.9 (or earlier) → Python 3.12 on Lambda
&lt;/h2&gt;

&lt;p&gt;You're doing this because AWS blocks &lt;code&gt;python3.9&lt;/code&gt; creates/updates Feb 1 / Mar 3 2027 (and earlier versions are already blocked). Python 3.12 removed a batch of stdlib modules in one release (PEP 632/594), so this path front-loads the most breakage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'distutils'&lt;/code&gt;&lt;/strong&gt; — removed in 3.12. &lt;a href="https://eolkits.com/fix/python-no-module-named-distutils/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'imp'&lt;/code&gt;&lt;/strong&gt; — removed in 3.12. &lt;a href="https://eolkits.com/fix/python-no-module-named-imp/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AttributeError: module 'collections' has no attribute 'Mapping'&lt;/code&gt;&lt;/strong&gt; — moved to &lt;code&gt;collections.abc&lt;/code&gt; (removed from &lt;code&gt;collections&lt;/code&gt; in 3.10, so this can also hit on the way to 3.10/3.11). &lt;a href="https://eolkits.com/fix/collections-has-no-attribute-mapping/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'smtpd'&lt;/code&gt;&lt;/strong&gt; — removed in 3.12. &lt;a href="https://eolkits.com/fix/python-no-module-named-smtpd/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'asyncore'&lt;/code&gt;&lt;/strong&gt; — removed in 3.12, usually alongside &lt;code&gt;smtpd&lt;/code&gt;. &lt;a href="https://eolkits.com/fix/python-no-module-named-asyncore/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DeprecationWarning: datetime.datetime.utcnow() is deprecated&lt;/code&gt;&lt;/strong&gt; — not fatal, but noisy, and worth fixing in the same pass. &lt;a href="https://eolkits.com/fix/datetime-utcnow-deprecated/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/lib64/libc.so.6: version 'GLIBC_2.28' not found&lt;/code&gt;&lt;/strong&gt; — if you ship any native dependency (&lt;code&gt;cryptography&lt;/code&gt;, &lt;code&gt;numpy&lt;/code&gt;, &lt;code&gt;psycopg2&lt;/code&gt;, &lt;code&gt;pydantic-core&lt;/code&gt;), moving to the AL2023-based Python 3.12 runtime (glibc 2.34) from an AL2-based one (glibc 2.26) can surface this separately from the stdlib removals above. &lt;a href="https://eolkits.com/fix/lambda-glibc-version-not-found/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Path 2: Python 3.11/3.12 → Python 3.13
&lt;/h2&gt;

&lt;p&gt;A second, smaller PEP 594 wave hits on the next hop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'cgi'&lt;/code&gt;&lt;/strong&gt; — removed in 3.13. &lt;a href="https://eolkits.com/fix/python-no-module-named-cgi/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'telnetlib'&lt;/code&gt;&lt;/strong&gt; — removed in 3.13. &lt;a href="https://eolkits.com/fix/python-no-module-named-telnetlib/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'crypt'&lt;/code&gt;&lt;/strong&gt; — removed in 3.13. &lt;a href="https://eolkits.com/fix/python-no-module-named-crypt/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ModuleNotFoundError: No module named 'lib2to3'&lt;/code&gt;&lt;/strong&gt; — removed in 3.13. &lt;a href="https://eolkits.com/fix/python-no-module-named-lib2to3/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And one that hits on the way &lt;em&gt;into&lt;/em&gt; 3.11, if you skipped straight past it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AttributeError: module 'asyncio' has no attribute 'coroutine'&lt;/code&gt;&lt;/strong&gt; — the legacy &lt;code&gt;@asyncio.coroutine&lt;/code&gt; decorator, removed in 3.11. &lt;a href="https://eolkits.com/fix/python-asyncio-has-no-attribute-coroutine/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Path 3: Node.js 16/18 → Node.js 20/22 on Lambda
&lt;/h2&gt;

&lt;p&gt;This path is less about stdlib removals and more about what the runtime stopped bundling and what OpenSSL 3 stopped allowing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Error: Cannot find module 'aws-sdk'&lt;/code&gt;&lt;/strong&gt; — AWS SDK v2 stopped shipping preinstalled from &lt;code&gt;nodejs18.x&lt;/code&gt; onward; only &lt;code&gt;@aws-sdk/*&lt;/code&gt; v3 is preinstalled now. This is usually the &lt;em&gt;first&lt;/em&gt; error you hit, at cold start. &lt;a href="https://eolkits.com/fix/node-cannot-find-module-aws-sdk/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Error: error:0308010C:digital envelope routines::unsupported&lt;/code&gt;&lt;/strong&gt; — OpenSSL 3 (bundled from Node 17+) rejecting a legacy hash, often from an older build tool (webpack 4). &lt;a href="https://eolkits.com/fix/node-error-digital-envelope-routines-unsupported/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;error:1E08010C:DECODER routines::unsupported&lt;/code&gt;&lt;/strong&gt; — OpenSSL 3 refusing to load a private key stored in a legacy format (PKCS#1, or encrypted with a weak cipher). &lt;a href="https://eolkits.com/fix/node-error-decoder-routines-unsupported/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Error: The module was compiled against a different Node.js version using NODE_MODULE_VERSION&lt;/code&gt;&lt;/strong&gt; — any native addon (&lt;code&gt;sharp&lt;/code&gt;, &lt;code&gt;bcrypt&lt;/code&gt;, &lt;code&gt;better-sqlite3&lt;/code&gt;) needs a rebuild for the new ABI. &lt;a href="https://eolkits.com/fix/node-module-version-mismatch/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Node Sass does not yet support your current environment&lt;/code&gt;&lt;/strong&gt; — node-sass specifically, since LibSass is dead and ships no Node 22 prebuild. &lt;a href="https://eolkits.com/fix/node-sass-deprecated-unsupported/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TypeError: crypto.createCipher is not a function&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;createCipher&lt;/code&gt;/&lt;code&gt;createDecipher&lt;/code&gt; were removed outright in Node 22 (not just deprecated). &lt;a href="https://eolkits.com/fix/node-crypto-createcipher-is-not-a-function/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[DEP0040] DeprecationWarning: The 'punycode' module is deprecated&lt;/code&gt;&lt;/strong&gt; — not fatal, usually from a transitive dependency, loudest on Node 22. &lt;a href="https://eolkits.com/fix/node-punycode-module-deprecated/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If none of the above matches and you're just staring at a bare &lt;code&gt;Runtime.ImportModuleError: Cannot find module&lt;/code&gt;, the cause could be any of #1 or #4 above, an esbuild &lt;code&gt;0.22+&lt;/code&gt; bundling default change, or a Lambda layer built on the wrong OS/arch — see the dedicated &lt;a href="https://eolkits.com/fix/lambda-runtime-importmoduleerror-cannot-find-module/" rel="noopener noreferrer"&gt;triage guide&lt;/a&gt; to tell them apart before guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Path 4: Amazon Linux 2 → Amazon Linux 2023
&lt;/h2&gt;

&lt;p&gt;AL2 reaches end of life June 30, 2026. This path isn't a Lambda runtime change at all — it's the base OS on EC2/ECS/EKS nodes — but it produces the same "one version bump, five unrelated-looking failures" shape:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;amazon-linux-extras: command not found&lt;/code&gt;&lt;/strong&gt; — the Extras Library mechanism doesn't exist on AL2023 at all. &lt;a href="https://eolkits.com/fix/amazon-linux-extras-command-not-found/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Error: Unable to find a match: &amp;lt;package&amp;gt;&lt;/code&gt;&lt;/strong&gt; — package renamed, version-namespaced, moved to SPAL, or dropped. &lt;a href="https://eolkits.com/fix/amazon-linux-2023-dnf-unable-to-find-a-match/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Failed to start ntpd.service: Unit ntpd.service not found&lt;/code&gt;&lt;/strong&gt; — AL2023 uses &lt;code&gt;chronyd&lt;/code&gt;. &lt;a href="https://eolkits.com/fix/amazon-linux-2023-ntpd-service-not-found/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Failed to start iptables.service: Unit iptables.service not found&lt;/code&gt;&lt;/strong&gt; — AL2023 defaults to nftables. &lt;a href="https://eolkits.com/fix/amazon-linux-2023-iptables-service-not-found/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/usr/bin/env: 'python2': No such file or directory&lt;/code&gt;&lt;/strong&gt; — AL2023 ships no Python 2 at all. &lt;a href="https://eolkits.com/fix/amazon-linux-2023-python2-command-not-found/" rel="noopener noreferrer"&gt;Fix →&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this exists as one page
&lt;/h2&gt;

&lt;p&gt;Each fix above already has its own full write-up — root cause, verified fix steps, primary source. What's usually missing when you're mid-migration isn't any single fix, it's &lt;em&gt;knowing what's coming next&lt;/em&gt; so you can batch the work instead of playing whack-a-mole one redeploy at a time. Bookmark whichever path applies to you and work down it in order.&lt;/p&gt;

&lt;p&gt;If you'd rather have this run against your actual account instead of reading it, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks in about 30 seconds, nothing uploaded — I maintain it, disclosing that plainly since it's the one link in this piece that isn't a fix.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>devops</category>
      <category>python</category>
    </item>
    <item>
      <title>"[DEP0040] DeprecationWarning: The `punycode` module is deprecated — what to do about it on Node.js 22 Lambda"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Thu, 30 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/dep0040-deprecationwarning-the-punycode-module-is-deprecated-what-to-do-about-it-on-nodejs-49n3</link>
      <guid>https://dev.to/ntoledo319/dep0040-deprecationwarning-the-punycode-module-is-deprecated-what-to-do-about-it-on-nodejs-49n3</guid>
      <description>&lt;p&gt;Upgrade a Lambda function to &lt;code&gt;nodejs22.x&lt;/code&gt; and CloudWatch fills up with a warning that wasn't there before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;(node:8) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody on the team imports &lt;code&gt;punycode&lt;/code&gt;. It's not in &lt;code&gt;package.json&lt;/code&gt;. The warning still shows up on every cold start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it appears now
&lt;/h2&gt;

&lt;p&gt;Node.js has shipped a built-in &lt;code&gt;punycode&lt;/code&gt; module since 0.x — it converts internationalized domain names between Unicode and ASCII (&lt;code&gt;xn--&lt;/code&gt;) form. Node deprecated the built-in copy in favor of the identical userland package back in Node 7, but the warning was quiet (&lt;code&gt;--pending-deprecation&lt;/code&gt; only) for years. Node 22 raises the volume: &lt;code&gt;DEP0040&lt;/code&gt; fires on stderr by default whenever anything in the process still resolves &lt;code&gt;require('punycode')&lt;/code&gt; — and it usually isn't your code doing the requiring.&lt;/p&gt;

&lt;p&gt;The most common source is a &lt;strong&gt;transitive dependency&lt;/strong&gt; that hasn't been updated: older major versions of &lt;code&gt;whatwg-url&lt;/code&gt; (used by &lt;code&gt;jsdom&lt;/code&gt;, &lt;code&gt;node-fetch&lt;/code&gt; polyfills, and various URL-parsing packages), some &lt;code&gt;url&lt;/code&gt;-parsing utilities, and legacy AWS SDK v2 internals all &lt;code&gt;require('punycode')&lt;/code&gt; directly instead of &lt;code&gt;require('punycode/')&lt;/code&gt; (note the trailing slash — that one character routes to the npm package instead of the deprecated Node built-in).&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the source
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--trace-deprecation&lt;/span&gt; your-handler.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--trace-deprecation&lt;/code&gt; prints a full stack trace with the warning instead of just the one-line message, which usually points straight at the offending &lt;code&gt;require()&lt;/code&gt; inside &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you'd rather not run the handler locally, grep for the bare require:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"require('punycode')"&lt;/span&gt; node_modules &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.js"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"punycode/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything that matches without the trailing slash is pulling in the deprecated built-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If it's your own code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;punycode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before — resolves to Node's deprecated built-in&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;punycode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;punycode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// After — resolves to the maintained userland package&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;punycode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;punycode/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trailing slash is not a typo — it's how Node's module resolver distinguishes the userland package (&lt;code&gt;node_modules/punycode/&lt;/code&gt;) from the built-in of the same name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If it's a dependency:&lt;/strong&gt; upgrade it. Recent major versions of &lt;code&gt;whatwg-url&lt;/code&gt;, &lt;code&gt;jsdom&lt;/code&gt;, and the AWS SDK v3 packages have all moved off the built-in. &lt;code&gt;npm ls punycode&lt;/code&gt; (or &lt;code&gt;npm why punycode&lt;/code&gt; on npm 9+) shows which installed package is pulling it in, so you know exactly what to bump.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you can't upgrade the dependency yet:&lt;/strong&gt; this is a warning, not a breaking error — the function still runs correctly. It's safe to ship a Node 22 migration with the warning present and clean it up separately. Don't let it block the runtime upgrade itself; deprecated-runtime block dates are the deadline that actually matters. But don't ignore it indefinitely either — &lt;code&gt;punycode&lt;/code&gt; is a real removal candidate in a future Node major, at which point it stops being a warning and starts being a crash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Silencing it (not recommended as a permanent fix)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--no-deprecation&lt;/span&gt; your-handler.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or set &lt;code&gt;NODE_NO_WARNINGS=1&lt;/code&gt; in the Lambda environment variables. This hides the &lt;em&gt;symptom&lt;/em&gt; — the transitive dependency is unchanged and will eventually need upgrading regardless. Useful for keeping noisy logs quiet during a migration window; not a substitute for actually tracking down the source.&lt;/p&gt;




&lt;p&gt;The free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; flags deprecated Lambda runtimes and known dependency-level breaks like this one from your &lt;code&gt;package.json&lt;/code&gt; / lockfile — in your browser, nothing uploaded; I maintain it. Full reference: &lt;strong&gt;&lt;a href="https://eolkits.com/fix/node-punycode-module-deprecated/" rel="noopener noreferrer"&gt;eolkits.com/fix/node-punycode-module-deprecated&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>node</category>
      <category>serverless</category>
    </item>
    <item>
      <title>"amazon-linux-extras: command not found — it's gone on Amazon Linux 2023"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Wed, 29 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/amazon-linux-extras-command-not-found-its-gone-on-amazon-linux-2023-28c7</link>
      <guid>https://dev.to/ntoledo319/amazon-linux-extras-command-not-found-its-gone-on-amazon-linux-2023-28c7</guid>
      <description>&lt;p&gt;You moved an AMI, Dockerfile, or user-data script from Amazon Linux 2 to Amazon Linux 2023, and a line that used to work now dies immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;bash: amazon-linux-extras: command not found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a missing package, not a stale repo — the &lt;code&gt;amazon-linux-extras&lt;/code&gt; mechanism itself doesn't exist on AL2023. There's nothing to install to bring it back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;Amazon Linux 2 shipped one old baseline of everything in its base repo, so AWS layered the &lt;strong&gt;Extras Library&lt;/strong&gt; on top — &lt;code&gt;amazon-linux-extras enable nginx1.20&lt;/code&gt;, &lt;code&gt;amazon-linux-extras install docker&lt;/code&gt;, and so on — to get newer versions of common packages without breaking the base image. Amazon Linux 2023 dropped that constraint (and the tool with it): its base &lt;code&gt;dnf&lt;/code&gt; repos ship current versions directly, so there's no separate "extras" layer to enable. Anything still calling &lt;code&gt;amazon-linux-extras&lt;/code&gt; — a golden-AMI build step, a Dockerfile &lt;code&gt;RUN&lt;/code&gt;, a cloud-init runcmd — has no command to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Stop calling &lt;code&gt;amazon-linux-extras&lt;/code&gt; on AL2023. What it used to gate now falls into one of three places:&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;# 1. Most former "extras" packages are just default dnf packages now&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker

&lt;span class="c"&gt;# 2. Version-specific ones are version-namespaced package names&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3.11
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx1.24
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; postgresql15

&lt;span class="c"&gt;# 3. The rest live in SPAL (Supplementary Packages for Amazon Linux) —&lt;/span&gt;
&lt;span class="c"&gt;#    enable it, then install as normal&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3.11  &lt;span class="c"&gt;# example: SPAL packages install like any other dnf package once the repo is present&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check which bucket your package landed in with a plain search before assuming it's gone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dnf search &amp;lt;package-name&amp;gt;
dnf list &lt;span class="nt"&gt;--available&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &amp;lt;package-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;dnf search&lt;/code&gt; and &lt;code&gt;dnf list --available&lt;/code&gt; both come up empty, it's likely an EPEL-only package now (see the related &lt;a href="https://eolkits.com/fix/amazon-linux-2023-dnf-unable-to-find-a-match/" rel="noopener noreferrer"&gt;&lt;code&gt;Unable to find a match&lt;/code&gt;&lt;/a&gt; error for that case) rather than an extras casualty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the automation, not just the instance
&lt;/h2&gt;

&lt;p&gt;The one-off fix is deleting the &lt;code&gt;amazon-linux-extras&lt;/code&gt; line from whatever's broken right now. The durable fix is finding every place that assumption is baked in before it breaks the next build:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"amazon-linux-extras"&lt;/span&gt; Dockerfile&lt;span class="k"&gt;*&lt;/span&gt; user-data/ ansible/ packer/ cloud-init/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace each hit with the direct &lt;code&gt;dnf install&lt;/code&gt; (or version-namespaced equivalent) for that package, and drop the &lt;code&gt;amazon-linux-extras enable ...&lt;/code&gt; step entirely — AL2023 has no topic to enable against.&lt;/p&gt;




&lt;p&gt;This is one piece of the broader Amazon Linux 2 → 2023 migration (package manager, firewall, time sync, and Python version all shift at once) — the full checklist is at &lt;strong&gt;&lt;a href="https://eolkits.com/fix/amazon-linux-2-eol/" rel="noopener noreferrer"&gt;eolkits.com/fix/amazon-linux-2-eol&lt;/a&gt;&lt;/strong&gt;. If you want to know which instances in your account are still on AL2 before support ends, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks in about 30 seconds — nothing uploaded, and I maintain it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>linux</category>
      <category>devops</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>"Failed to start ntpd.service — Amazon Linux 2023 replaced it with chrony"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Tue, 28 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/failed-to-start-ntpdservice-amazon-linux-2023-replaced-it-with-chrony-16ca</link>
      <guid>https://dev.to/ntoledo319/failed-to-start-ntpdservice-amazon-linux-2023-replaced-it-with-chrony-16ca</guid>
      <description>&lt;p&gt;You moved an AMI, launch template, or Ansible playbook from Amazon Linux 2 to Amazon Linux 2023, and time-sync provisioning that used to just work now fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Failed to start ntpd.service: Unit ntpd.service not found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The instance boots fine, &lt;code&gt;dnf&lt;/code&gt; works, but whatever enabled or started &lt;code&gt;ntpd.service&lt;/code&gt; — user-data, cloud-init, a Packer build, a config-management run — has nothing to attach to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;Amazon Linux 2 shipped &lt;code&gt;ntp&lt;/code&gt;/&lt;code&gt;ntpd&lt;/code&gt; for time synchronization. Amazon Linux 2023 doesn't install that package or unit by default — it standardizes on &lt;strong&gt;chrony&lt;/strong&gt; (&lt;code&gt;chronyd&lt;/code&gt;) instead, the same time-sync daemon most modern Linux distros have already moved to. Anything that assumes &lt;code&gt;ntpd.service&lt;/code&gt; exists — a &lt;code&gt;systemctl enable --now ntpd&lt;/code&gt; line baked into a golden AMI, a cloud-init runcmd, an Ansible task targeting &lt;code&gt;ntp.conf&lt;/code&gt; — has no unit to enable on AL2023.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install and enable chrony (often already present on AL2023 AMIs — check first)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; chrony
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; chronyd

&lt;span class="c"&gt;# Verify it's syncing&lt;/span&gt;
chronyc sources
chronyc tracking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you had custom NTP servers configured in &lt;code&gt;/etc/ntp.conf&lt;/code&gt;, migrate the &lt;code&gt;server&lt;/code&gt;/&lt;code&gt;pool&lt;/code&gt; lines to &lt;code&gt;/etc/chrony.conf&lt;/code&gt; (chrony's syntax is close enough that most entries port over as-is — just double-check any &lt;code&gt;iburst&lt;/code&gt;/&lt;code&gt;prefer&lt;/code&gt; flags still parse).&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the automation, not just the instance
&lt;/h2&gt;

&lt;p&gt;A one-time fix on a running instance doesn't help if the same user-data or Ansible role provisions the next hundred instances off a stale AL2 assumption. Grep provisioning code for the old unit name and update it once at the source:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"ntpd.service&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;ntp&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;conf&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;dnf install.*ntp[^d]&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;yum install.*ntp[^d]"&lt;/span&gt; user-data/ ansible/ packer/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;systemctl enable --now ntpd&lt;/code&gt; with &lt;code&gt;systemctl enable --now chronyd&lt;/code&gt;, and drop any &lt;code&gt;dnf install ntp&lt;/code&gt; line — that package isn't what AL2023 uses for this.&lt;/p&gt;




&lt;p&gt;This is one piece of the broader Amazon Linux 2 → 2023 migration (package manager, firewall, time sync, and Python version all shift at once) — the full checklist is at &lt;strong&gt;&lt;a href="https://eolkits.com/fix/amazon-linux-2-eol/" rel="noopener noreferrer"&gt;eolkits.com/fix/amazon-linux-2-eol&lt;/a&gt;&lt;/strong&gt;. If you want to know which instances in your account are still on AL2 before support ends, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks in about 30 seconds — nothing uploaded, and I maintain it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>linux</category>
      <category>devops</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>"/usr/bin/env: 'python2': No such file or directory — Amazon Linux 2023 has no Python 2"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Mon, 27 Jul 2026 07:17:39 +0000</pubDate>
      <link>https://dev.to/ntoledo319/usrbinenv-python2-no-such-file-or-directory-amazon-linux-2023-has-no-python-2-5h8g</link>
      <guid>https://dev.to/ntoledo319/usrbinenv-python2-no-such-file-or-directory-amazon-linux-2023-has-no-python-2-5h8g</guid>
      <description>&lt;p&gt;You moved an instance, AMI, or launch template from Amazon Linux 2 to Amazon Linux 2023, and something that used to run cleanly now fails on first boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;/usr/bin/env: 'python2': No such file or directory
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if the script called the interpreter directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;-bash: /usr/bin/python2: No such file or directory
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dnf&lt;/code&gt; works, the instance is healthy, but any script with a &lt;code&gt;#!/usr/bin/python2&lt;/code&gt; shebang, a &lt;code&gt;python2 script.py&lt;/code&gt; invocation, or a tool that shells out to &lt;code&gt;python2&lt;/code&gt;/&lt;code&gt;pip2&lt;/code&gt; under the hood has nothing to run against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;Amazon Linux 2 shipped Python 2.7 as part of the base image, because plenty of legacy system tooling and older cloud-init modules still expected it. Amazon Linux 2023 made a clean break: &lt;strong&gt;there is no Python 2 package at all&lt;/strong&gt; — not &lt;code&gt;python2&lt;/code&gt;, not &lt;code&gt;python27&lt;/code&gt;, nothing installable via &lt;code&gt;dnf&lt;/code&gt;. AL2023 ships Python 3 (currently 3.9 as the default &lt;code&gt;python3&lt;/code&gt;, with versioned packages like &lt;code&gt;python3.11&lt;/code&gt;/&lt;code&gt;python3.12&lt;/code&gt; available) and nothing older.&lt;/p&gt;

&lt;p&gt;This isn't a deprecation warning you can silence — the interpreter binary simply isn't in the AL2023 package repos. Anything that assumes it exists breaks the moment you provision on the new AMI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user-data script with a &lt;code&gt;#!/usr/bin/env python2&lt;/code&gt; shebang&lt;/li&gt;
&lt;li&gt;A legacy cron job or systemd unit calling &lt;code&gt;python2 /opt/app/run.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Config management (Ansible, Chef, Puppet) with a module that shells out to &lt;code&gt;python2&lt;/code&gt;/&lt;code&gt;pip2&lt;/code&gt; internally&lt;/li&gt;
&lt;li&gt;A vendored tool or SDK that was never ported off Python 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;There is no supported way to install Python 2 on Amazon Linux 2023 — the only real path is porting the code to Python 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — point the shebang/interpreter at Python 3:&lt;/strong&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="c"&gt;# before&lt;/span&gt;
&lt;span class="c"&gt;#!/usr/bin/python2&lt;/span&gt;

&lt;span class="c"&gt;# after&lt;/span&gt;
&lt;span class="c"&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 — install a specific Python 3 minor version if your code depends on one:&lt;/strong&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;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3.11
&lt;span class="c"&gt;# or: python3.12, depending on what you've tested against&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3 — update anything that invokes &lt;code&gt;python2&lt;/code&gt;/&lt;code&gt;pip2&lt;/code&gt; explicitly:&lt;/strong&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"python2&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;pip2"&lt;/span&gt; user-data/ cron.d/ ansible/ systemd/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace each hit with &lt;code&gt;python3&lt;/code&gt;/&lt;code&gt;pip3&lt;/code&gt; (or the versioned binary from step 2), and re-test — Python 2→3 porting issues (print statements, &lt;code&gt;unicode&lt;/code&gt; vs &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;dict.iteritems()&lt;/code&gt;, integer division) are a separate, well-documented migration, not an AL2023-specific one; the &lt;a href="https://docs.python.org/3/howto/pyporting.html" rel="noopener noreferrer"&gt;Python 3 porting guide&lt;/a&gt; covers the language-level changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — check for silent failures, not just missing-binary errors:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the Python-2-dependent step is wrapped in a script that swallows its exit code (&lt;code&gt;|| true&lt;/code&gt;, a bare &lt;code&gt;except:&lt;/code&gt;), you may not see the "No such file" error at all — just a feature that quietly stopped working. Grep first, don't wait for a support ticket.&lt;/p&gt;




&lt;p&gt;This is one piece of the broader Amazon Linux 2 → 2023 migration (package manager, firewall, time sync, and Python version all shift at once) — the full checklist is at &lt;strong&gt;&lt;a href="https://eolkits.com/fix/amazon-linux-2-eol/" rel="noopener noreferrer"&gt;eolkits.com/fix/amazon-linux-2-eol&lt;/a&gt;&lt;/strong&gt;. If you want to know which instances in your account are still on AL2 before support ends, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks in about 30 seconds — nothing uploaded, and I maintain it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>"Node Sass does not yet support your current environment — what breaks when you upgrade to nodejs22.x"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Mon, 27 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/node-sass-does-not-yet-support-your-current-environment-what-breaks-when-you-upgrade-to-44d6</link>
      <guid>https://dev.to/ntoledo319/node-sass-does-not-yet-support-your-current-environment-what-breaks-when-you-upgrade-to-44d6</guid>
      <description>&lt;p&gt;You upgrade a Lambda function, a Docker base image, or your CI pipeline to Node.js 18 or 22 — and the next &lt;code&gt;npm install&lt;/code&gt; or Docker build dies 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;Error: Node Sass does not yet support your current environment:
  Linux 64-bit with Unsupported runtime (v22)

For more information on which environments are supported please see:
https://github.com/sass/node-sass/releases/tag/v9.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if the binary was already installed on an older runtime and the container now runs Node 22:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: The module '/app/node_modules/node-sass/vendor/linux-x64-127/binding.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 108. This version of Node.js requires
NODE_MODULE_VERSION 127.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both are the same root cause. Here's what's happening and how to fix it permanently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;node-sass&lt;/code&gt; is a Node.js wrapper around &lt;strong&gt;LibSass&lt;/strong&gt; — a C++ implementation of the Sass compiler. Because it's a native addon, it must ship a precompiled binary for each Node.js major version and platform combination. When it can't find one at install time, it falls back to compiling from source with &lt;code&gt;node-gyp&lt;/code&gt;, which usually fails in CI or Lambda build environments that don't have the right build tools.&lt;/p&gt;

&lt;p&gt;The core problem: &lt;strong&gt;LibSass and node-sass are end-of-life.&lt;/strong&gt; The Sass team deprecated LibSass in October 2020 and archived the &lt;code&gt;sass/node-sass&lt;/code&gt; GitHub repository in July 2024. No new prebuilt binaries are released for Node.js 18, 20, or 22. The package is frozen.&lt;/p&gt;

&lt;p&gt;This surfaces specifically during Lambda runtime upgrades because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nodejs16.x&lt;/code&gt; shipped Node.js 16 (ABI version 93) — old enough that node-sass had a prebuild.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nodejs18.x&lt;/code&gt; ships Node.js 18 (ABI 108). No node-sass prebuild.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nodejs22.x&lt;/code&gt; ships Node.js 22 (ABI 127). No node-sass prebuild, and the repo is archived.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The moment you flip the runtime and rebuild your deployment package, the install fails or the previously-built native binary refuses to load.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: migrate to Dart Sass
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;sass&lt;/code&gt; package is &lt;strong&gt;Dart Sass&lt;/strong&gt; — the official, actively-maintained Sass implementation. It compiles to pure JavaScript: no native addon, no prebuilt binary, no ABI mismatch, no node-gyp. It installs cleanly on every Node.js version from 12 onward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — swap the package:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall node-sass
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; sass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole migration for most projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — update webpack's sass-loader (if you use it):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Older &lt;code&gt;sass-loader&lt;/code&gt; versions would try &lt;code&gt;node-sass&lt;/code&gt; first. With &lt;code&gt;node-sass&lt;/code&gt; removed, it auto-detects &lt;code&gt;sass&lt;/code&gt; — but if you have an explicit &lt;code&gt;implementation&lt;/code&gt; option in your webpack config, update it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// webpack.config.js — before&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sass-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-sass&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// webpack.config.js — after&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sass-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sass&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3 — Angular CLI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Angular projects that specify &lt;code&gt;node-sass&lt;/code&gt; in the &lt;code&gt;stylePreprocessorOptions&lt;/code&gt; or have it as a dependency can swap to &lt;code&gt;sass&lt;/code&gt; with no config change — Angular CLI auto-detects the installed implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — react-scripts / Create React App:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CRA uses &lt;code&gt;sass&lt;/code&gt; as the recommended processor. Remove &lt;code&gt;node-sass&lt;/code&gt;, install &lt;code&gt;sass&lt;/code&gt;, and the build picks it up automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compatibility: is it a drop-in?
&lt;/h2&gt;

&lt;p&gt;For the overwhelming majority of real-world SCSS codebases, yes. The only behavioral differences you might hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/&lt;/code&gt; division in Sass&lt;/strong&gt; — Dart Sass warns that &lt;code&gt;/&lt;/code&gt; is deprecated as a division operator inside calc-style expressions (use &lt;code&gt;math.div()&lt;/code&gt; from &lt;code&gt;sass:math&lt;/code&gt; instead). This is a deprecation warning, not a breakage — your build still works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!global&lt;/code&gt; in nested scopes&lt;/strong&gt; — a rarely-used pattern; Dart Sass handles it consistently with the spec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indented syntax (&lt;code&gt;.sass&lt;/code&gt; files)&lt;/strong&gt; — fully supported.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your tests pass after the swap, you're done. Run &lt;code&gt;npx sass --version&lt;/code&gt; to confirm you're on Dart Sass (it prints &lt;code&gt;X.Y.Z compiled with dart2js&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Lambda-specific check: build the package on the right runtime
&lt;/h2&gt;

&lt;p&gt;After the migration, make sure your deployment package is built on the same Node.js version Lambda will run:&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;# Build inside the Lambda container image to guarantee ABI match&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app &lt;span class="nt"&gt;-w&lt;/span&gt; /app &lt;span class="se"&gt;\&lt;/span&gt;
  public.ecr.aws/lambda/nodejs:22 &lt;span class="se"&gt;\&lt;/span&gt;
  npm ci &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;sass&lt;/code&gt; is pure JS, this is less critical than before — but it's still good practice for any Lambda package containing native modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeline: why this hits now
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Node.js&lt;/th&gt;
&lt;th&gt;Last node-sass prebuild&lt;/th&gt;
&lt;th&gt;Phase 2 (create-block)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nodejs16.x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;✓ v9.0.0&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nodejs18.x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;✗ archived&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nodejs20.x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;✗ archived&lt;/td&gt;
&lt;td&gt;~late 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nodejs22.x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;✗ archived&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AWS blocks creating new functions on deprecated runtimes first, then blocks updating existing ones. &lt;code&gt;nodejs16.x&lt;/code&gt; is already fully blocked. Teams migrating away from it are hitting this node-sass wall now.&lt;/p&gt;




&lt;p&gt;The free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; detects deprecated Lambda runtimes and known packages that require rebuilt binaries after a runtime upgrade — in your browser, nothing uploaded. Full per-error fixes: &lt;strong&gt;&lt;a href="https://eolkits.com/fix/node-sass-deprecated-unsupported/" rel="noopener noreferrer"&gt;eolkits.com/fix/node-sass-deprecated-unsupported&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>"ModuleNotFoundError: No module named 'smtpd'" / "'asyncore'" — Python 3.12's PEP 594 stdlib cleanup</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Sun, 26 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/modulenotfounderror-no-module-named-smtpd-asyncore-python-312s-pep-594-stdlib-cleanup-2mad</link>
      <guid>https://dev.to/ntoledo319/modulenotfounderror-no-module-named-smtpd-asyncore-python-312s-pep-594-stdlib-cleanup-2mad</guid>
      <description>&lt;p&gt;You moved a Lambda function (or any service) to the python3.12 runtime and something that worked fine on 3.11 now dies at import time with one of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ModuleNotFoundError: No module named 'smtpd'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ModuleNotFoundError: No module named 'asyncore'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in your code changed. These modules are just gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;smtpd&lt;/code&gt;, &lt;code&gt;asyncore&lt;/code&gt;, and &lt;code&gt;asynchat&lt;/code&gt; were all deprecated in Python 3.6 with explicit &lt;code&gt;DeprecationWarning&lt;/code&gt;s, and all three were removed outright in Python 3.12 as part of &lt;a href="https://docs.python.org/3/whatsnew/3.12.html" rel="noopener noreferrer"&gt;PEP 594&lt;/a&gt; — the standard library's long-running "dead batteries" cleanup (the same PEP that also dropped &lt;code&gt;distutils&lt;/code&gt;, &lt;code&gt;imp&lt;/code&gt;, and several others).&lt;/p&gt;

&lt;p&gt;They tend to surface in a few specific places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;smtpd&lt;/code&gt;&lt;/strong&gt; — a local &lt;code&gt;DebuggingServer&lt;/code&gt; or test SMTP server used in integration tests, or a script that fakes outbound mail for local development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;asyncore&lt;/code&gt; / &lt;code&gt;asynchat&lt;/code&gt;&lt;/strong&gt; — older network code written before &lt;code&gt;asyncio&lt;/code&gt; existed, or a dependency (often something mail- or FTP-related) that never migrated off the callback-based event loop these modules implemented.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way, the pattern is the same one behind most Python-3.12 Lambda breakage: code that was already deprecated-but-working for years suddenly hard-fails the moment the runtime bump turns the warning into a missing import.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix — smtpd
&lt;/h2&gt;

&lt;p&gt;Replace it with &lt;code&gt;aiosmtpd&lt;/code&gt;, the maintained asyncio-based successor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;aiosmtpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before (removed in 3.12)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;smtpd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DebuggingServer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncore&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DebuggingServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1025&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;asyncore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# After
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aiosmtpd.controller&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrintHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_DATA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;envelope&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;envelope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;250 Message accepted for delivery&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PrintHandler&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1025&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you only need the removed module back verbatim rather than rewriting call sites, the &lt;code&gt;standard-smtpd&lt;/code&gt; package on PyPI restores it as a drop-in shim — a faster stopgap, not the long-term fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix — asyncore / asynchat
&lt;/h2&gt;

&lt;p&gt;Port the code to &lt;code&gt;asyncio&lt;/code&gt;, which is the supported replacement for the transport/protocol model &lt;code&gt;asyncore&lt;/code&gt; implemented:&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="c1"&gt;# Before (removed in 3.12): asyncore.dispatcher subclass with handle_read/handle_write
# After: an asyncio.Protocol
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyProtocol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;data_received&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# equivalent to handle_read
&lt;/span&gt;        &lt;span class="bp"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connection_lost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# equivalent to handle_close
&lt;/span&gt;        &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the import comes from a third-party dependency rather than your own code, upgrade it first — most actively maintained libraries that used &lt;code&gt;asyncore&lt;/code&gt; shipped an asyncio-based release well before 3.12 removed it. For a genuinely unmaintained dependency, vendoring the pure-Python &lt;code&gt;asyncore.py&lt;/code&gt;/&lt;code&gt;asynchat.py&lt;/code&gt; from a Python 3.11 source tree is a working (if inelegant) stopgap while you plan the real port.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catch every PEP 594 removal before the deploy, not after
&lt;/h2&gt;

&lt;p&gt;Both of these tend to arrive together, and rarely alone — a codebase old enough to import &lt;code&gt;smtpd&lt;/code&gt; or &lt;code&gt;asyncore&lt;/code&gt; is usually old enough to still import &lt;code&gt;imp&lt;/code&gt; or rely on &lt;code&gt;distutils&lt;/code&gt; too. Smoke-test the whole surface on 3.12 before flipping the runtime label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3.12 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import your_module"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that against every entry point (handler, layer, and any script the deploy pipeline invokes) surfaces the full removal list at once, instead of one &lt;code&gt;ModuleNotFoundError&lt;/code&gt; per cold start.&lt;/p&gt;




&lt;p&gt;Free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; flags every deprecated Python/Node Lambda runtime in an account in about 30 seconds, nothing uploaded — I maintain it. Full references: &lt;strong&gt;&lt;a href="https://eolkits.com/fix/python-no-module-named-smtpd/" rel="noopener noreferrer"&gt;eolkits.com/fix/python-no-module-named-smtpd&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://eolkits.com/fix/python-no-module-named-asyncore/" rel="noopener noreferrer"&gt;eolkits.com/fix/python-no-module-named-asyncore&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>aws</category>
      <category>lambda</category>
      <category>migration</category>
    </item>
    <item>
      <title>"Failed to start iptables.service — Amazon Linux 2023 dropped it for nftables"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Sat, 25 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/failed-to-start-iptablesservice-amazon-linux-2023-dropped-it-for-nftables-5flo</link>
      <guid>https://dev.to/ntoledo319/failed-to-start-iptablesservice-amazon-linux-2023-dropped-it-for-nftables-5flo</guid>
      <description>&lt;p&gt;You moved an AMI, launch template, or Ansible playbook from Amazon Linux 2 to Amazon Linux 2023, and a step that used to just work now fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Failed to start iptables.service: Unit iptables.service not found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The instance is healthy, &lt;code&gt;dnf&lt;/code&gt; works, but whatever enabled or started &lt;code&gt;iptables.service&lt;/code&gt; — user-data, cloud-init, a Packer build, a config-management run — breaks on first boot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;On Amazon Linux 2, the &lt;code&gt;iptables-services&lt;/code&gt; package provided an &lt;code&gt;iptables.service&lt;/code&gt; unit that saved and restored firewall rules across reboots (&lt;code&gt;iptables-save&lt;/code&gt; / &lt;code&gt;iptables-restore&lt;/code&gt; under systemd). Amazon Linux 2023 doesn't install that package or unit by default — it standardizes on &lt;strong&gt;nftables&lt;/strong&gt; as the default firewall backend instead. Anything that assumes &lt;code&gt;iptables.service&lt;/code&gt; exists — a &lt;code&gt;systemctl enable --now iptables&lt;/code&gt; line baked into a golden AMI, a cloud-init runcmd, an Ansible task — has nothing to attach to on AL2023.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;iptables&lt;/code&gt; command itself isn't gone (there's an &lt;code&gt;iptables-nft&lt;/code&gt; compatibility layer), but the systemd unit your automation expects is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, two ways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Move to native nftables (recommended for anything new):&lt;/strong&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="c"&gt;# Convert existing iptables rules to nftables syntax&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables-save &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/rules.v4
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables-translate &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/rules.v4 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/nftables/rules.nft   &lt;span class="c"&gt;# per-rule; wrap in a table/chain if starting fresh&lt;/span&gt;

&lt;span class="c"&gt;# Enable and start the nftables service instead of iptables.service&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; nftables

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nft list ruleset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;iptables-translate&lt;/code&gt; converts rules one at a time — for anything beyond a handful of rules, review the generated nftables syntax rather than trusting a blind bulk conversion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep iptables syntax via the compatibility layer (faster migration, same commands):&lt;/strong&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;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; iptables-nft
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules written with &lt;code&gt;iptables&lt;/code&gt;/&lt;code&gt;ip6tables&lt;/code&gt; commands are transparently stored in the nftables backend. You keep your existing &lt;code&gt;iptables -A ...&lt;/code&gt; scripts, but &lt;strong&gt;persistence is managed by the &lt;code&gt;nftables&lt;/code&gt; service&lt;/strong&gt;, not &lt;code&gt;iptables.service&lt;/code&gt; — update any &lt;code&gt;systemctl enable iptables&lt;/code&gt; reference to &lt;code&gt;systemctl enable nftables&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the automation, not just the instance
&lt;/h2&gt;

&lt;p&gt;The one-time instance fix doesn't help if the same user-data or Ansible role provisions the next hundred instances. Grep your provisioning code for the old assumption and update it once at the source:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"iptables.service&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;iptables-services"&lt;/span&gt; user-data/ ansible/ packer/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;systemctl enable --now iptables&lt;/code&gt; with &lt;code&gt;systemctl enable --now nftables&lt;/code&gt; (or the &lt;code&gt;iptables-nft&lt;/code&gt; equivalent), and drop any &lt;code&gt;dnf install iptables-services&lt;/code&gt; line — that package doesn't exist on AL2023.&lt;/p&gt;




&lt;p&gt;This is one piece of the broader Amazon Linux 2 → 2023 migration (package manager, firewall, time sync, and Python version all shift at once) — the full checklist is at &lt;strong&gt;&lt;a href="https://eolkits.com/fix/amazon-linux-2-eol/" rel="noopener noreferrer"&gt;eolkits.com/fix/amazon-linux-2-eol&lt;/a&gt;&lt;/strong&gt;. If you want to know which instances in your account are still on AL2 before support ends, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks in about 30 seconds — nothing uploaded, and I maintain it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>linux</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>"Error: Unable to find a match — fixing dnf package lookups after migrating off Amazon Linux 2"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Fri, 24 Jul 2026 07:17:38 +0000</pubDate>
      <link>https://dev.to/ntoledo319/error-unable-to-find-a-match-fixing-dnf-package-lookups-after-migrating-off-amazon-linux-2-56c7</link>
      <guid>https://dev.to/ntoledo319/error-unable-to-find-a-match-fixing-dnf-package-lookups-after-migrating-off-amazon-linux-2-56c7</guid>
      <description>&lt;p&gt;You moved an instance, AMI, or user-data script from Amazon Linux 2 to Amazon Linux 2023, and a package install that's worked for years suddenly fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Unable to find a match: python3-devel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(or whatever package your script names). The instance boots fine, &lt;code&gt;dnf&lt;/code&gt; works, but this one package is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;Amazon Linux 2023's repositories aren't the same tree as AL2's — they were rebuilt, and along the way packages got renamed, version-namespaced, moved to a supplementary repo, or dropped outright:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version-namespaced.&lt;/strong&gt; AL2023 ships multiple versions of things side by side instead of one default, so the plain name is gone in favor of a versioned one: &lt;code&gt;python3&lt;/code&gt; → &lt;code&gt;python3.11&lt;/code&gt; (or &lt;code&gt;python3.12&lt;/code&gt;), &lt;code&gt;nginx&lt;/code&gt; → &lt;code&gt;nginx1.24&lt;/code&gt;, &lt;code&gt;postgresql&lt;/code&gt; → &lt;code&gt;postgresql15&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moved out of the base repo.&lt;/strong&gt; Packages that lived in &lt;code&gt;amazon-linux-extras&lt;/code&gt; on AL2 (which doesn't exist on AL2023 at all) are now either default &lt;code&gt;dnf&lt;/code&gt; packages, version-namespaced ones, or live in &lt;strong&gt;SPAL&lt;/strong&gt; (Supplementary Packages for Amazon Linux) — a repo you have to enable separately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moved to EPEL.&lt;/strong&gt; Some AL2 packages were never Amazon's to begin with and only ever came from EPEL; if your script assumed they'd resolve from the base AL2023 repos, they won't until EPEL is enabled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actually removed.&lt;/strong&gt; A handful of packages just aren't packaged for AL2023 at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scripts that hardcode an AL2-era name and run &lt;code&gt;yum install &amp;lt;name&amp;gt;&lt;/code&gt; (which on AL2023 is a symlink straight to &lt;code&gt;dnf&lt;/code&gt;) hit this the moment they run against the new repo set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the real name
&lt;/h2&gt;

&lt;p&gt;Don't guess — ask dnf directly:&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;# Search by keyword&lt;/span&gt;
dnf search python3-devel

&lt;span class="c"&gt;# Search by the actual binary/file you need, if you know it&lt;/span&gt;
dnf provides &lt;span class="s1"&gt;'*/pip3'&lt;/span&gt;
dnf provides &lt;span class="s1"&gt;'*/nginx'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dnf provides&lt;/code&gt; is the more reliable of the two when you know the file the old package used to install (a binary, a &lt;code&gt;.so&lt;/code&gt;, a config path) but not what the new package is called.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, by cause
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Version-namespaced package:&lt;/strong&gt; install the versioned name directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3.11 python3.11-devel   &lt;span class="c"&gt;# or python3.12&lt;/span&gt;
dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx1.24
dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; postgresql15 postgresql15-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Used to come from &lt;code&gt;amazon-linux-extras&lt;/code&gt;:&lt;/strong&gt; that command doesn't exist on AL2023 — there's nothing to enable it against. Check whether the package is now a default &lt;code&gt;dnf&lt;/code&gt; package, a version-namespaced one, or lives in SPAL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dnf repository-packages amazon-linux-extras-equivalent-repo list  &lt;span class="c"&gt;# naming varies by topic; check SPAL docs for the exact repo id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice most former &lt;code&gt;amazon-linux-extras&lt;/code&gt; topics (nginx, php, docker CLIs, etc.) are just direct or version-namespaced &lt;code&gt;dnf install&lt;/code&gt;s on AL2023 now — the extras layer was there because AL2's base repo shipped one old version of everything; AL2023's doesn't have that constraint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Genuinely EPEL-only:&lt;/strong&gt; enable EPEL for AL2023, then install normally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &amp;lt;package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Confirm the EPEL release package that matches AL2023's current EPEL support before pinning a version — the AL2023 user guide documents which EPEL release to use.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually dropped, no replacement:&lt;/strong&gt; vendor a static binary, build from source against AL2023's toolchain, or containerize the workload instead of installing it as a system package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you standardize on a name
&lt;/h2&gt;

&lt;p&gt;AL2023 versions packages more aggressively than AL2 did, which means today's &lt;code&gt;python3.11&lt;/code&gt; might not be tomorrow's default. Check a package's support window before hardcoding it into a golden AMI or long-lived user-data script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; dnf-plugin-support-info
dnf support-info &amp;lt;package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells you how long the specific package version is supported — useful for avoiding a repeat of this exact migration in a couple of years.&lt;/p&gt;




&lt;p&gt;This is one piece of the broader Amazon Linux 2 → 2023 migration (package manager, firewall, time sync, and Python version all shift at once) — the full checklist is at &lt;strong&gt;&lt;a href="https://eolkits.com/fix/amazon-linux-2-eol/" rel="noopener noreferrer"&gt;eolkits.com/fix/amazon-linux-2-eol&lt;/a&gt;&lt;/strong&gt;. If you want to know which instances in your account are still on AL2 before support ends, the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks in about 30 seconds — nothing uploaded, and I maintain it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>linux</category>
      <category>devops</category>
      <category>dnf</category>
    </item>
    <item>
      <title>"Runtime.ImportModuleError: Cannot find module — a triage guide for Lambda runtime upgrades"</title>
      <dc:creator>ntoledo319</dc:creator>
      <pubDate>Fri, 24 Jul 2026 07:17:03 +0000</pubDate>
      <link>https://dev.to/ntoledo319/runtimeimportmoduleerror-cannot-find-module-a-triage-guide-for-lambda-runtime-upgrades-1jj1</link>
      <guid>https://dev.to/ntoledo319/runtimeimportmoduleerror-cannot-find-module-a-triage-guide-for-lambda-runtime-upgrades-1jj1</guid>
      <description>&lt;p&gt;You bumped a Lambda function to a newer runtime, redeployed, and the first invocation dies at cold start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Runtime.ImportModuleError: Error: Cannot find module 'some-package'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Python's version reads &lt;code&gt;Unable to import module 'handler': No module named 'some_package'&lt;/code&gt; — same underlying category of failure.)&lt;/p&gt;

&lt;p&gt;The unhelpful part: this exact message is the symptom of at least four different root causes, and none of them are code bugs — your handler didn't change. Here's how to tell them apart before you start guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The triage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Is the missing module &lt;code&gt;aws-sdk&lt;/code&gt; (lowercase, no &lt;code&gt;@&lt;/code&gt;) and did you move to &lt;code&gt;nodejs18.x&lt;/code&gt; or later?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's not a bundling problem — AWS stopped preinstalling AWS SDK v2 in the runtime starting with &lt;code&gt;nodejs18.x&lt;/code&gt;. Only &lt;code&gt;@aws-sdk/*&lt;/code&gt; v3 modular clients ship now. This has a dedicated fix with the full v2→v3 migration: &lt;strong&gt;&lt;a href="https://eolkits.com/fix/node-cannot-find-module-aws-sdk/" rel="noopener noreferrer"&gt;eolkits.com/fix/node-cannot-find-module-aws-sdk&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Is the module genuinely in &lt;code&gt;package.json&lt;/code&gt;/&lt;code&gt;requirements.txt&lt;/code&gt;, and did you switch bundlers or bump esbuild recently?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;esbuild &lt;code&gt;0.22+&lt;/code&gt; changed its default to treat &lt;code&gt;node_modules&lt;/code&gt; as external — meaning a real dependency can still get silently excluded from the deployment ZIP. If you use SAM, CDK, or Serverless Framework with the esbuild build method, force it back to inline:&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="c1"&gt;# SAM template.yaml&lt;/span&gt;
&lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or &lt;code&gt;esbuild --packages=bundle&lt;/code&gt; directly. Confirm by unzipping your build artifact and checking whether the module directory is actually present under &lt;code&gt;node_modules/&lt;/code&gt; — don't trust the build log alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Does the error only happen through a Lambda Layer, and did you build that layer on your laptop or a generic CI image?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A layer built on the wrong base image is a structure/path problem, not a missing-package problem: Node expects &lt;code&gt;nodejs/node_modules/&amp;lt;package&amp;gt;&lt;/code&gt; inside the layer zip, Python expects &lt;code&gt;python/&amp;lt;package&amp;gt;&lt;/code&gt;. If the layer was zipped with a different top-level folder, or built for the wrong architecture (&lt;code&gt;x86_64&lt;/code&gt; vs &lt;code&gt;arm64&lt;/code&gt;), the runtime won't find it even though the package is technically "in" the layer. Rebuild inside the actual Lambda base image for your target runtime and architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/opt public.ecr.aws/lambda/nodejs:22 &lt;span class="se"&gt;\&lt;/span&gt;
  npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--prefix&lt;/span&gt; /opt/nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Does the module load fine on your machine but fail only on Lambda, and does it involve a native binary (&lt;code&gt;.node&lt;/code&gt; file, or a Python package like &lt;code&gt;psycopg2&lt;/code&gt;, &lt;code&gt;numpy&lt;/code&gt;, &lt;code&gt;pydantic-core&lt;/code&gt;, &lt;code&gt;grpcio&lt;/code&gt;)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's usually not a "cannot find" problem at the module-resolution level at all — it's the loader finding the file and then failing to link it, which Node/Python often still surfaces as an import error. This is a glibc/architecture mismatch between where the binary was compiled and the runtime's base OS. Full diagnosis and fix: &lt;strong&gt;&lt;a href="https://eolkits.com/fix/lambda-glibc-version-not-found/" rel="noopener noreferrer"&gt;eolkits.com/fix/lambda-glibc-version-not-found&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  If none of the four fit
&lt;/h2&gt;

&lt;p&gt;Check the boring things first, in order: does the &lt;code&gt;Handler&lt;/code&gt; setting (&lt;code&gt;file.export&lt;/code&gt; for Node, &lt;code&gt;file.function&lt;/code&gt; for Python) still match where the file actually lives in the deployment package after the runtime change moved your build output layout? Did the runtime change also change the working directory the bundler assumes (&lt;code&gt;/var/task&lt;/code&gt; vs &lt;code&gt;/opt&lt;/code&gt;)? A surprising fraction of "phantom" ImportModuleErrors are a handler path pointing at a file that no longer exists at that path post-rebuild, not a missing dependency at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catching this before a cold start in production does
&lt;/h2&gt;

&lt;p&gt;All four causes above are detectable from the deployment artifact itself, before you ever invoke the function — which matters because ImportModuleError only surfaces at cold start, so a broken deploy can sit "successful" for minutes or hours before anyone notices.&lt;/p&gt;

&lt;p&gt;If you're tracking deprecated-runtime exposure across an account already, the same scan pass is a natural place to also flag native-dependency ABI mismatches and stale bundler configs before a redeploy trips one of these: the free &lt;strong&gt;&lt;a href="https://eolkits.com/scan" rel="noopener noreferrer"&gt;EOLkits scanner&lt;/a&gt;&lt;/strong&gt; checks an account in about 30 seconds, nothing uploaded — I maintain it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>node</category>
      <category>python</category>
    </item>
  </channel>
</rss>
