<?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: Atharva Unde</title>
    <description>The latest articles on DEV Community by Atharva Unde (@atharvaunde).</description>
    <link>https://dev.to/atharvaunde</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%2F2762428%2F8e921033-436b-4e3a-ae71-db308ce9af74.jpg</url>
      <title>DEV Community: Atharva Unde</title>
      <link>https://dev.to/atharvaunde</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atharvaunde"/>
    <language>en</language>
    <item>
      <title>Your Cache Is Making Things Worse</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sun, 07 Jun 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/your-cache-is-making-things-worse-3a6a</link>
      <guid>https://dev.to/atharvaunde/your-cache-is-making-things-worse-3a6a</guid>
      <description>&lt;h2&gt;
  
  
  The Seductive Lie of Caching
&lt;/h2&gt;

&lt;p&gt;Teams love caching. It feels like free performance. Add Redis, &lt;em&gt;cache everything&lt;/em&gt;, boom—10x faster. Everyone's happy. Except it's not free. And faster isn't always better.&lt;/p&gt;

&lt;p&gt;Bad caching creates more problems than it solves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User makes a payment, sees it pending for 2 hours (cached stale data)&lt;/li&gt;
&lt;li&gt;You deploy code, but old cached values are still served to half your users&lt;/li&gt;
&lt;li&gt;Cache hit rate is 40% because TTL is wrong&lt;/li&gt;
&lt;li&gt;Redis memory fills up and starts evicting random keys&lt;/li&gt;
&lt;li&gt;You spend 3 hours debugging "why is this endpoint returning the wrong data?" (it's the cache)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real cost of caching isn't the cache hit. It's the cache miss you didn't plan for.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Framework: When to Cache
&lt;/h2&gt;

&lt;p&gt;Ask one question before caching anything:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"If this data is wrong for 30 seconds, does it break something?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If yes, don't cache. Or cache very short TTL. If no, cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safe to Cache
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;User preferences (can be wrong for an hour)&lt;/li&gt;
&lt;li&gt;Public data (can be wrong for a day)&lt;/li&gt;
&lt;li&gt;Computed reports (can be wrong for 5 minutes)&lt;/li&gt;
&lt;li&gt;Product catalogs (can be wrong for 10 minutes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Not Safe to Cache
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Payment status (wrong for 1 second = problem)&lt;/li&gt;
&lt;li&gt;User balance (wrong for 1 second = problem)&lt;/li&gt;
&lt;li&gt;Authorization decisions (wrong for 1 second = problem)&lt;/li&gt;
&lt;li&gt;Session state (wrong for 1 second = problem)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern: anything where freshness is critical, don't cache aggressively.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Problem: Cache Invalidation
&lt;/h2&gt;

&lt;p&gt;There are only two hard things in Computer Science: cache invalidation and naming things. Teams underestimate this. You cache something. Great. Now you need to invalidate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do you invalidate?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Option 1: Automatic TTL&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple, but serves stale data&lt;/li&gt;
&lt;li&gt;Good when staleness is acceptable&lt;/li&gt;
&lt;li&gt;Bad when freshness is critical&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Option 2: Invalidate on writes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex, but fresh data&lt;/li&gt;
&lt;li&gt;Good when updates are infrequent&lt;/li&gt;
&lt;li&gt;Bad when updates are frequent (invalidate more than cache hit)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Option 3: Event-based invalidation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most complex, but flexible&lt;/li&gt;
&lt;li&gt;Good for distributed systems&lt;/li&gt;
&lt;li&gt;Bad for tightly coupled systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The mistake:&lt;/em&gt; Choosing invalidation strategy after caching is already deployed. Choose it first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Caching Database Queries Without Invalidation Strategy
&lt;/h3&gt;

&lt;p&gt;You cache user.find(userId). Great. User updates their email. Oops. Cache still has old email. Now you invalidate: remove cache when user is updated. Great.&lt;/p&gt;

&lt;p&gt;Now user updates email, then profile picture. Two cache invalidations? Or one? Distributed system? Now you invalidate on 3 services. One service misses the invalidation message.&lt;/p&gt;

&lt;p&gt;This is why cache invalidation is hard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 2: Using Redis as a Database
&lt;/h3&gt;

&lt;p&gt;Redis is fast. So teams use it for persistence. Then the server crashes. Redis data is gone. Or: Redis fills up. System deletes random keys. Or: Redis replicates wrong. Data is inconsistent across nodes.&lt;/p&gt;

&lt;p&gt;Use Redis as a cache (&lt;em&gt;data loss acceptable&lt;/em&gt;) or use a real database (&lt;em&gt;data loss not acceptable&lt;/em&gt;). Don't use it as both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: Caching Expensive Computations Without Measuring
&lt;/h3&gt;

&lt;p&gt;You have an expensive database query. Takes 500ms. Cache it. Cache hit drops it to 1ms from Redis.&lt;/p&gt;

&lt;p&gt;Except: the network call to Redis takes 5ms. Cache miss takes 510ms (cache miss + computation). Cache hit rate is 60%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Average latency:&lt;/strong&gt; (0.6 × 5ms) + (0.4 × 510ms) = 207ms.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Without cache:&lt;/strong&gt; 500ms.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;With cache:&lt;/strong&gt; 207ms.&lt;/p&gt;

&lt;p&gt;Better? Yes. But you never measured. You just assumed caching helps.&lt;/p&gt;

&lt;p&gt;Measure actual latency impact, not just cache hit rate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 4: Not Setting Max Memory Policy
&lt;/h3&gt;

&lt;p&gt;Redis memory fills up. What happens? By default: Redis stops accepting writes. System breaks. You configured it: evict least-recently-used keys. Now old data disappears unexpectedly. You configured it: evict random keys. Even worse.&lt;/p&gt;

&lt;p&gt;Know what your max memory policy is. Don't let it be a surprise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 5: Assuming Cache Hits Always Improve Latency
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Network latency to Redis:&lt;/strong&gt; 5ms&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Network latency to database:&lt;/strong&gt; 50ms&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Database query time:&lt;/strong&gt; 400ms&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache hit latency:&lt;/strong&gt; 5ms&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cache miss latency:&lt;/strong&gt; 455ms&lt;br&gt;&lt;br&gt;
&lt;strong&gt;No cache latency:&lt;/strong&gt; 450ms&lt;/p&gt;

&lt;p&gt;Cache hit saves 445ms. Great. But cache miss is slower than no cache. So your average depends on hit rate.&lt;/p&gt;

&lt;p&gt;If hit rate drops below 50%, caching is overhead.&lt;/p&gt;

&lt;p&gt;Measure actual latency, not hit rate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Framework That Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Only cache what's safe to be stale&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Freshness requirement determines TTL&lt;/li&gt;
&lt;li&gt;Payment? 0 cache or 10 second TTL max&lt;/li&gt;
&lt;li&gt;User preference? 1 hour cache is fine&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have invalidation strategy before deployment&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;TTL? Event-based? On-write invalidation?&lt;/li&gt;
&lt;li&gt;Don't say "we'll figure it out"&lt;/li&gt;
&lt;li&gt;Invalidation complexity should influence your cache decision&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure actual impact&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Don't trust hit rate&lt;/li&gt;
&lt;li&gt;Measure latency with and without cache&lt;/li&gt;
&lt;li&gt;Measure memory cost&lt;/li&gt;
&lt;li&gt;If benefit is small, remove cache&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Know when caching makes things worse&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Distributed system with eventual consistency? Caching amplifies the problem&lt;/li&gt;
&lt;li&gt;High memory cost for low benefit? Remove it&lt;/li&gt;
&lt;li&gt;Debugging takes 10x longer? Not worth it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor cache behavior in production&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Hit rate vs latency&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Eviction rate&lt;/li&gt;
&lt;li&gt;If behavior changes, investigate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  When Not to Cache
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Payment systems (use real-time data)&lt;/li&gt;
&lt;li&gt;Authorization (use real-time data)&lt;/li&gt;
&lt;li&gt;Distributed systems with complex invalidation (keep it simple)&lt;/li&gt;
&lt;li&gt;Data that changes frequently but you cache anyway (you'll serve lies)&lt;/li&gt;
&lt;li&gt;Just to mask slow databases (fix the database instead)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Real Insight
&lt;/h2&gt;

&lt;p&gt;Caching is optimizing for the wrong thing. You cache a database query to make it faster. But why is the query slow?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bad indexes? Fix it&lt;/li&gt;
&lt;li&gt;N+1 queries? Fix it&lt;/li&gt;
&lt;li&gt;Missing pagination? Fix it&lt;/li&gt;
&lt;li&gt;Bad query logic? Fix it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most teams add caching to mask poor database design. Fix the root cause. Use caching for genuinely expensive operations that are unavoidable.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Only cache what's safe to be stale.&lt;/strong&gt; If data needs to be fresh, don't cache aggressively. &lt;strong&gt;Have invalidation strategy first.&lt;/strong&gt; Choose TTL, on-write, or event-based before you cache. &lt;strong&gt;Measure actual impact.&lt;/strong&gt; Hit rate is meaningless. Measure latency and memory cost. &lt;strong&gt;Don't cache to mask slow databases.&lt;/strong&gt; Fix the database. &lt;strong&gt;Monitor in production.&lt;/strong&gt; If caching behavior changes, investigate.&lt;/p&gt;

&lt;p&gt;Bad caching creates more problems than it solves. Good caching is invisible because the trade-offs are understood and managed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: caching · Redis · performance · backend · architecture · database optimization · distributed systems · DevOps&lt;/em&gt;&lt;/p&gt;

</description>
      <category>caching</category>
      <category>redis</category>
      <category>performance</category>
      <category>backend</category>
    </item>
    <item>
      <title>The Vendor Lock-in Dilemma: Speed vs. Flexibility in Cloud Architecture</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/the-vendor-lock-in-dilemma-speed-vs-flexibility-in-cloud-architecture-1ejl</link>
      <guid>https://dev.to/atharvaunde/the-vendor-lock-in-dilemma-speed-vs-flexibility-in-cloud-architecture-1ejl</guid>
      <description>&lt;h2&gt;
  
  
  A Question That Won't Go Away
&lt;/h2&gt;

&lt;p&gt;I get asked this question a lot: "When you're building a side project or working on something, how do you decide what to use?"&lt;/p&gt;

&lt;p&gt;The conversation usually goes like this:&lt;/p&gt;

&lt;p&gt;People are debating database engines (MongoDB vs. PostgreSQL), event-driven architecture (do we need message queues?), caching strategies (Redis? ElastiCache? Self-hosted?).&lt;/p&gt;

&lt;p&gt;But beneath every specific question lurks something bigger:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do we move fast with vendor-locked proprietary solutions, or invest in open-source alternatives and keep our options open?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I rarely see teams or builders ask this thoroughly enough. Most just drift toward vendor lock-in without realizing it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Vendor Services Are So Seductive
&lt;/h2&gt;

&lt;p&gt;Let's be honest: vendor-specific managed services are &lt;em&gt;seductive&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Take MongoDB. It's open-source, portable, you can self-host it anywhere. But AWS offers DocumentDB. Azure offers CosmosDB. Google offers Firestore. They all claim to be drop-in replacements and promise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster time to market&lt;/li&gt;
&lt;li&gt;Built-in high availability&lt;/li&gt;
&lt;li&gt;Managed backups and patching&lt;/li&gt;
&lt;li&gt;Integrated monitoring and security&lt;/li&gt;
&lt;li&gt;Attractive per-unit pricing (at first)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same pattern repeats everywhere. SQS looks unbeatable for messaging-managed, reliable, integrated with your AWS ecosystem. Why would you self-host RabbitMQ or Kafka when AWS handles all operational burden?&lt;/p&gt;

&lt;p&gt;On paper, it's a no-brainer. In reality, it's where most teams paint themselves into corners.&lt;/p&gt;




&lt;h2&gt;
  
  
  The One Question You Should Ask Before Deciding
&lt;/h2&gt;

&lt;p&gt;Whenever I face this decision, I ask one thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What's our long-term commitment to our cloud provider?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This question sounds straightforward. It's not.&lt;/p&gt;

&lt;p&gt;Behind it sits a minefield of unknowns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Are we locked into a long-term contract?&lt;/strong&gt; Is there a volume discount that makes switching cost-prohibitive?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's our flexibility if we need to migrate?&lt;/strong&gt; If geopolitical events, legal constraints, or a data breach force us to leave AWS tomorrow, how painful is that?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How deeply is our codebase coupled to vendor APIs?&lt;/strong&gt; If we use DynamoDB instead of MongoDB, how much code only works with AWS?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the real cost of exit?&lt;/strong&gt; Data transfer fees are just the start. The rewrite is the killer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've watched organizations face these questions &lt;em&gt;during&lt;/em&gt; a crisis, not before. By then, it's catastrophically expensive.&lt;/p&gt;

&lt;p&gt;Most teams never ask at all. They just drift toward lock-in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Three Real Options
&lt;/h2&gt;

&lt;p&gt;You have three paths. Each has honest trade-offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Pure Vendor Lock-in (DynamoDB, SQS, DocumentDB)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fastest implementation&lt;/li&gt;
&lt;li&gt;Zero operational overhead&lt;/li&gt;
&lt;li&gt;Deep AWS integration&lt;/li&gt;
&lt;li&gt;Native features you won't find elsewhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero flexibility once committed&lt;/li&gt;
&lt;li&gt;Pricing can change unilaterally (and usually does)&lt;/li&gt;
&lt;li&gt;Migration becomes a career-defining project&lt;/li&gt;
&lt;li&gt;You're betting the company on one vendor's roadmap&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 2: Self-Hosted Everything (Kafka, RabbitMQ, PostgreSQL on EC2)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete vendor independence&lt;/li&gt;
&lt;li&gt;Full control over every variable&lt;/li&gt;
&lt;li&gt;True multi-cloud portability&lt;/li&gt;
&lt;li&gt;No surprise price hikes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You become the database team&lt;/li&gt;
&lt;li&gt;Operational complexity scales fast&lt;/li&gt;
&lt;li&gt;On-call burden for infrastructure failures&lt;/li&gt;
&lt;li&gt;Hidden costs in personnel and time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 3: Managed Open-Source (AWS-managed RabbitMQ, Azure Database for PostgreSQL)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operational relief without vendor lock-in&lt;/li&gt;
&lt;li&gt;Uses community standards, not proprietary APIs&lt;/li&gt;
&lt;li&gt;Easier migration if you need to switch providers&lt;/li&gt;
&lt;li&gt;High availability without managing it yourself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher per-unit cost than self-hosted&lt;/li&gt;
&lt;li&gt;Still some vendor dependency (but less severe)&lt;/li&gt;
&lt;li&gt;Fewer cutting-edge features than pure vendor offerings&lt;/li&gt;
&lt;li&gt;Still requires some operational knowledge&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How I Actually Decide
&lt;/h2&gt;

&lt;p&gt;Here's my decision framework. Use it when the team's split on this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask in order:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is there any realistic scenario where we might change cloud providers? (Hint: The answer is usually yes.)&lt;/li&gt;
&lt;li&gt;If that happened, how much pain could we actually absorb?&lt;/li&gt;
&lt;li&gt;What's the probability × impact of that scenario?&lt;/li&gt;
&lt;li&gt;How much extra would Option 3 (managed open-source) cost monthly?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If managed open-source costs 15% more per month but saves you a multi-quarter migration, it's almost always worth it.&lt;/p&gt;

&lt;p&gt;Run the math. Compare the monthly overhead to the cost of being stuck.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Lock-in Actually Breaks
&lt;/h2&gt;

&lt;p&gt;I've watched teams chase every new AWS feature without questioning the cost. A feature ships, everyone wants it, it gets integrated everywhere. Six months later, we're realizing we've painted ourselves into a corner.&lt;/p&gt;

&lt;p&gt;But more critically, I've seen real crises:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Geopolitical constraints&lt;/strong&gt; forcing data residency changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal actions&lt;/strong&gt; requiring migration off a specific provider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Competitive pricing&lt;/strong&gt; suddenly making a different provider's economics irresistible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract negotiations&lt;/strong&gt; where vendor lock-in becomes a liability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The teams that had chosen portable solutions? A few weeks of work.&lt;/p&gt;

&lt;p&gt;The teams that had optimized purely for speed? Months of crisis mode, expensive rewrites, and careers defined by technical debt blowing up.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Matters
&lt;/h2&gt;

&lt;p&gt;The question isn't "should we use vendor lock-in?" It's:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How much flexibility do we need to sleep at night?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Different organizations answer differently. A startup with 6 months of runway might reasonably choose speed over portability. An enterprise that can't afford surprises invests in optionality.&lt;/p&gt;

&lt;p&gt;What most teams do-defaulting to lock-in without ever consciously asking the question-that's the trap.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;The next time you're in an architecture discussion and someone says "Let's use DocumentDB because it's managed and fast," ask the question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What's our long-term commitment to AWS?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Listen to the answer. Really listen.&lt;/p&gt;

&lt;p&gt;Sometimes the slowest path to launch is the fastest path to long-term survival. And sometimes accepting operational complexity today prevents existential crises tomorrow.&lt;/p&gt;

&lt;p&gt;The trick is knowing which is which-and asking before you're in crisis mode.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: vendor lock-in · cloud architecture · AWS · managed services · DynamoDB · multi-cloud · infrastructure decisions · scaling · RabbitMQ · database selection · DevOps strategy&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vendorlockin</category>
      <category>cloudarchitecture</category>
      <category>aws</category>
      <category>managedservices</category>
    </item>
    <item>
      <title>Stop Hardcoding AWS Keys.</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/stop-hardcoding-aws-keys-53g4</link>
      <guid>https://dev.to/atharvaunde/stop-hardcoding-aws-keys-53g4</guid>
      <description>&lt;h2&gt;
  
  
  The Problem With Hardcoded Keys
&lt;/h2&gt;

&lt;p&gt;When you hardcode AWS access keys and secret keys into your Node.js application, you create a management burden:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manual Key Rotation:&lt;/strong&gt; You have to remember to rotate them. Nobody does this consistently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret Storage:&lt;/strong&gt; You need to store them somewhere (env file, secrets manager, config). Each adds complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Trail:&lt;/strong&gt; If a key leaks, you need to know who has it and where it's being used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk of Exposure:&lt;/strong&gt; Every time you export, backup, or move code, you risk exposing credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment Friction:&lt;/strong&gt; You have to inject secrets into every environment (dev, staging, prod).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any of those keys get exposed (committed to git, logged, or leaked), you have to revoke them immediately and update everywhere they're used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's operational debt.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Instance Profiles (for EC2)
&lt;/h2&gt;

&lt;p&gt;An instance profile is a container for an IAM role. When you attach an instance profile to an EC2 instance, applications running on that instance can retrieve temporary credentials from the instance metadata service instead of using static keys.&lt;/p&gt;

&lt;p&gt;Your Node.js application doesn't need hardcoded keys. The AWS SDK automatically retrieves temporary credentials through the instance metadata service.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Create an IAM Role (e.g., "NodeAppRole")
   ↓
2. Attach IAM policies to it (e.g., S3ReadOnly, DynamoDBAccess)
   ↓
3. Create an Instance Profile containing the IAM Role
   ↓
4. Launch your EC2 instance with that Instance Profile
   ↓
5. Applications retrieve temporary credentials from instance metadata
   ↓
6. Node.js SDK automatically picks them up from the credential chain
   ↓
7. Your code makes AWS API calls. Done.

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

&lt;/div&gt;



&lt;p&gt;No keys in your code. No env files. No secrets management for credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Node.js Automatically Picks Up Credentials
&lt;/h2&gt;

&lt;p&gt;The AWS SDK for JavaScript v3 uses a default credential provider chain. It automatically checks multiple sources for credentials, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Shared credentials file (~/.aws/credentials)&lt;/li&gt;
&lt;li&gt;IAM Identity Center&lt;/li&gt;
&lt;li&gt;Web identity tokens&lt;/li&gt;
&lt;li&gt;ECS task role credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 instance metadata service&lt;/strong&gt; (if running on EC2)&lt;/li&gt;
&lt;li&gt;And others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you attach an instance profile to an EC2 instance, the SDK automatically discovers and uses the credentials from the instance metadata service.&lt;/p&gt;

&lt;p&gt;Your code doesn't need configuration or changes:&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;// This works automatically if running on an EC2 instance with an Instance Profile&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListBucketsCommand&lt;/span&gt; &lt;span class="p"&gt;}&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="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ListBucketsCommand&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The SDK fetches credentials from the metadata service behind the scenes. You don't do anything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Instance Profiles vs Hardcoded Keys: Side by Side
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hardcoded Keys Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DON'T DO THIS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListBucketsCommand&lt;/span&gt; &lt;span class="p"&gt;}&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="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_SECRET_ACCESS_KEY&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ListBucketsCommand&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&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;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keys in environment variables (must be injected at runtime)&lt;/li&gt;
&lt;li&gt;Keys in .env file (risk of accidental commit)&lt;/li&gt;
&lt;li&gt;Rotating keys means redeploying application&lt;/li&gt;
&lt;li&gt;Manual tracking of which environments have which keys&lt;/li&gt;
&lt;li&gt;If key is compromised, chaos ensues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Instance Profile Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DO THIS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListBucketsCommand&lt;/span&gt; &lt;span class="p"&gt;}&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="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// No credentials configuration needed. SDK auto-discovers from instance profile.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ListBucketsCommand&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&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;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No keys in code, env vars, or config files&lt;/li&gt;
&lt;li&gt;Temporary credentials (AWS manages them automatically for the role)&lt;/li&gt;
&lt;li&gt;Fine-grained permissions (one role per application)&lt;/li&gt;
&lt;li&gt;Audit trail (CloudTrail tracks which role did what)&lt;/li&gt;
&lt;li&gt;Zero application changes for credential management&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  For EKS (Kubernetes)
&lt;/h2&gt;

&lt;p&gt;For EKS workloads, use &lt;strong&gt;IAM Roles for Service Accounts (IRSA)&lt;/strong&gt; instead of instance profiles.&lt;/p&gt;

&lt;p&gt;IRSA works by associating an IAM role with a Kubernetes service account. Pods that use that service account can assume the role and obtain temporary credentials.&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;# Create a ServiceAccount with an IAM role associated via annotation&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-service-account&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;eks.amazonaws.com/role-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::ACCOUNT:role/AppRole&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Your Node.js pod automatically gets credentials through the AWS SDK's credential chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; IRSA provides better isolation than relying on the EC2 instance metadata service directly. However, if the instance metadata service is not restricted, pods may still be able to access the node's IAM role through IMDS. AWS recommends using IMDSv2 and restricting metadata service access to prevent unintended access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Operational Burden:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardcoded keys = you manage rotation manually&lt;/li&gt;
&lt;li&gt;Instance Profiles = AWS manages temporary credentials automatically for the role on the instance &lt;strong&gt;Security:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Hardcoded keys = static credentials, high blast radius if leaked&lt;/li&gt;
&lt;li&gt;Instance Profiles = temporary credentials, automatically rotated, limited scope &lt;strong&gt;Auditability:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Hardcoded keys = hard to track (which deployment has which key?)&lt;/li&gt;
&lt;li&gt;Instance Profiles = CloudTrail shows exactly which role did what and when &lt;strong&gt;Scalability:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Hardcoded keys = doesn't scale beyond a handful of environments&lt;/li&gt;
&lt;li&gt;Instance Profiles = scales to hundreds of services and environments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Setup Example (Quick)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create IAM Role
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Role Name: NodeAppRole
Trusted Entity: EC2 service (ec2.amazonaws.com)
Permissions: Attach policies your app needs (S3, DynamoDB, etc.)

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create Instance Profile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Instance Profile Name: NodeAppProfile
Attach Role: NodeAppRole

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Launch EC2 Instance
&lt;/h3&gt;

&lt;p&gt;When launching, specify the Instance Profile. Your app automatically has credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Code (No Changes Needed)
&lt;/h3&gt;

&lt;p&gt;Your Node.js code works as-is. The SDK finds credentials automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake #1:&lt;/strong&gt; Still setting AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why it's wrong:&lt;/strong&gt; Defeats the purpose. You're back to managing keys manually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What to do:&lt;/strong&gt; Remove them. Let the SDK auto-discover from Instance Metadata. &lt;strong&gt;Mistake #2:&lt;/strong&gt; Using Instance Profile on dev machine but hardcoded keys in CI/CD.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it's wrong:&lt;/strong&gt; Inconsistency. Some environments have keys, some don't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What to do:&lt;/strong&gt; Use Instance Profiles everywhere. For local dev, use AWS credentials file or assume a role. &lt;strong&gt;Mistake #3:&lt;/strong&gt; One broad Instance Profile for all applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it's wrong:&lt;/strong&gt; Violates least-privilege principle. If one app is compromised, all can access the same resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What to do:&lt;/strong&gt; Create granular roles. One role per application, with only the permissions it needs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Hardcoded AWS keys require manual rotation, create security risks, and add operational burden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Attach an IAM Role to your EC2 instance (or use IRSA on EKS). Your Node.js app automatically gets temporary credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; No keys in code. No env vars. No management. AWS handles credential rotation for you.&lt;/p&gt;

&lt;p&gt;If you're still hardcoding keys, stop. Instance Profiles solve this problem completely.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: AWS · Security · Node.js · IAM · Backend · Infrastructure · BestPractices · Credentials&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>node</category>
      <category>iam</category>
    </item>
    <item>
      <title>I Panicked Over Nothing (And It Took Me An Hour To Realize)</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/i-panicked-over-nothing-and-it-took-me-an-hour-to-realize-2nco</link>
      <guid>https://dev.to/atharvaunde/i-panicked-over-nothing-and-it-took-me-an-hour-to-realize-2nco</guid>
      <description>&lt;h2&gt;
  
  
  The Setup (Where I Thought I Was Smart)
&lt;/h2&gt;

&lt;p&gt;Four years ago, I was fresh at my job. Still figuring out Kubernetes. Still learning DevOps. But I was eager to prove I knew what I was doing.&lt;/p&gt;

&lt;p&gt;So I volunteered to set up the lower environment cluster on AWS. You know, the cluster developers use for testing before pushing to production.&lt;/p&gt;

&lt;p&gt;I built it from scratch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Infrastructure Stack for EKS Migration:&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; EKS cluster with eksctl YAML
&lt;span class="p"&gt;-&lt;/span&gt; Private subnets for worker nodes
&lt;span class="p"&gt;-&lt;/span&gt; NAT gateway for egress
&lt;span class="p"&gt;-&lt;/span&gt; Microservices deployed (being tested by developers)
&lt;span class="p"&gt;-&lt;/span&gt; IPv4-only VPC
&lt;span class="p"&gt;-&lt;/span&gt; NGINX Ingress Controller
&lt;span class="p"&gt;-&lt;/span&gt; AWS ACM for SSL/TLS
&lt;span class="p"&gt;-&lt;/span&gt; Network Load Balancer
&lt;span class="p"&gt;-&lt;/span&gt; CloudFront distribution in front
&lt;span class="p"&gt;-&lt;/span&gt; Route 53 A record pointing to CloudFront

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

&lt;/div&gt;



&lt;p&gt;And it worked. Everything worked perfectly. I was feeling like a real DevOps engineer. Then the messages started.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Incident
&lt;/h2&gt;

&lt;p&gt;We were migrating from EC2 to Kubernetes. I'd set up the EKS cluster and asked developers to test the microservices while I finished deploying the rest.&lt;/p&gt;

&lt;p&gt;They reported the domain wouldn't resolve. On certain networks, browsers showed ERR_NAME_NOT_RESOLVED or DNS_PROBE_FINISHED_NXDOMAIN. But it worked fine from my machine and office network.&lt;/p&gt;

&lt;p&gt;Some networks: broken. Others: fine.&lt;/p&gt;

&lt;p&gt;I was sweating.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Panic Phase
&lt;/h2&gt;

&lt;p&gt;Everything checked out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application logs → Fine
Kubernetes events → Normal
Cluster health → Good
Load balancer → Healthy
Route 53 DNS records → All there
CloudFront settings → Correct

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

&lt;/div&gt;



&lt;p&gt;But developers were still getting resolution failures. Works from some networks (IPv4), fails from others (IPv6-only).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern was screaming at me. I just wasn't listening.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Rabbit Hole
&lt;/h2&gt;

&lt;p&gt;I debugged the symptom, not the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First 15 minutes:&lt;/strong&gt; checked DNS. Records were there. CNAME was correct. CloudFront alias configured. But I kept debugging because that's where the error came from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next 20 minutes:&lt;/strong&gt; tried different DNS providers. Changed to Google Public DNS. Flushed cache. Still broken from some networks, fine from others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next 15 minutes:&lt;/strong&gt; blamed CloudFront. Checked everything. Tried cache invalidation. Tried recreating the distribution.&lt;/p&gt;

&lt;p&gt;Then I started spiraling. Route 53 routing policy? Load balancer misconfigured? Rebuild the cluster?&lt;/p&gt;

&lt;p&gt;One hour in. No progress. Just deeper into the wrong layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment I Stopped (And Actually Thought)
&lt;/h2&gt;

&lt;p&gt;Then it hit me: &lt;strong&gt;Why does it work from network A but not network B?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's not a DNS question. That's not a CloudFront question. That's a &lt;strong&gt;network connectivity question.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd been debugging the wrong layer the whole time.&lt;/p&gt;

&lt;p&gt;So I did what I should have done 45 minutes earlier. I checked my network:&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="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;From the problematic network:
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://ipv4.icanhazip.com
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;no response, &lt;span class="nb"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://ipv6.icanhazip.com
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2409:XXXX:XXXX::1 ✓
&lt;span class="go"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's when it hit me.&lt;/strong&gt; I only had IPv6 from that network. No IPv4. But my entire cluster was IPv4-only.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Realization
&lt;/h2&gt;

&lt;p&gt;I had only created an A record (IPv4) in Route 53. Developers on IPv6-only networks had no way to resolve the domain.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IPv6-only developer
    ↓
Looks up mydomain.com
    ↓
Route 53 returns only A record
    ↓
"I don't have IPv4, can't use this"
    ↓
ERR_NAME_NOT_RESOLVED

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The answer was staring at me. I was debugging the wrong layer.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;Add an AAAA record (IPv6) to Route 53 pointing to CloudFront.&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="s"&gt;mydomain.com&lt;/span&gt;
  &lt;span class="s"&gt;A record → CloudFront&lt;/span&gt;
  &lt;span class="s"&gt;AAAA record → CloudFront&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now IPv6-only clients resolve the domain and CloudFront handles the dual-stack translation. Took 5 minutes to fix. Took 55 minutes to find.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Lesson
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When something breaks unevenly, don't debug the error. Debug the difference.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I focused on the symptom (DNS resolution failed). I should have asked: why does it work from network A but not network B?&lt;/p&gt;

&lt;p&gt;The difference tells you the problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works: Networks with IPv4&lt;/li&gt;
&lt;li&gt;Fails: Networks with IPv6 only&lt;/li&gt;
&lt;li&gt;Problem: Only IPv4 DNS records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One question. That's all it took.&lt;/p&gt;

&lt;p&gt;Most engineers debug vertically-deeper into the same layer. Good engineers debug horizontally-they find what's different between working and broken.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Should Know
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When something breaks:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't panic. Seriously. Panic makes you stupid.&lt;/li&gt;
&lt;li&gt;Check the obvious (logs, configs, health).&lt;/li&gt;
&lt;li&gt;If that's fine, zoom out. What's different between working and broken?&lt;/li&gt;
&lt;li&gt;Debug from the affected user's perspective, not your machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Wrong vs Right:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WRONG: "The error says DNS_PROBE_FINISHED_NXDOMAIN, so I'll debug DNS"&lt;/li&gt;
&lt;li&gt;RIGHT: "Works from IPv4 networks, fails from IPv6 networks. Why?"&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Timeline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;14:30 - Error report
14:35 - Check logs (fine)
14:40 - Check Kubernetes (fine)
14:50 - Blame DNS (waste 15 min)
15:05 - Try different DNS provider (waste 15 min)
15:20 - Blame CloudFront (waste 15 min)
15:35 - Finally ask: "Why does it work here but not there?"
15:40 - Realize: only A record, no AAAA record
15:45 - Add AAAA record to Route 53
15:50 - Test: Works

Total: 1 hour 20 minutes
Time wasted debugging wrong layer: 50 minutes
Time to actually fix: 5 minutes

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;You'll have incidents. You'll panic. That's normal.&lt;/p&gt;

&lt;p&gt;The engineers who get ahead stop in the middle of the panic and ask: &lt;strong&gt;"Am I looking at the problem or the symptom?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Symptom: DNS resolution failed&lt;br&gt;&lt;br&gt;
Problem: Missing AAAA record (IPv6 DNS)&lt;/p&gt;

&lt;p&gt;I spent an hour on the symptom. Five minutes on the problem would have fixed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; EKS cluster worked from IPv4 networks, failed from IPv6-only networks.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;My Mistake:&lt;/strong&gt; Debugged DNS, CloudFront, Kubernetes-the wrong layers.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Root Cause:&lt;/strong&gt; Only created A record (IPv4). No AAAA record (IPv6).&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Add AAAA record to Route 53.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Pattern:&lt;/strong&gt; When something breaks unevenly, debug the difference-not the error.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: DevOps · Kubernetes · AWS · EKS · Debugging · IPv6 · IPv4 · CloudFront · Network · Lessons Learned · Infrastructure&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>aws</category>
      <category>eks</category>
    </item>
    <item>
      <title>Communication Is a Skill Engineers Can't Afford to Ignore</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sun, 17 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/communication-is-a-skill-engineers-cant-afford-to-ignore-338n</link>
      <guid>https://dev.to/atharvaunde/communication-is-a-skill-engineers-cant-afford-to-ignore-338n</guid>
      <description>&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;Imagine you work at a company that builds online courses. You've got an in-house learning management system (LMS) hosting proprietary course content. Videos live in S3 buckets and various video providers, protected by a DRM service managed by a third-party vendor.&lt;/p&gt;

&lt;p&gt;One day, someone walks into your office: "Before you joined, our team wasn't well-coordinated. It's possible the DRM provider also bundles video storage we don't use. Can you check if any of our videos ended up there?"&lt;/p&gt;

&lt;p&gt;Simple question. Messy answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Happened
&lt;/h2&gt;

&lt;p&gt;You dig in and discover: A founding team member uploaded videos to the vendor's storage as a temporary measure, then left. No documentation. No tracking.&lt;/p&gt;

&lt;p&gt;Weeks later, the vendor experiences infrastructure issues. Their problems cascade upstream to your DRM service, which breaks your LMS. The chain of causation is so tangled it takes hours to figure out why everything went down.&lt;/p&gt;

&lt;p&gt;Now you need to tell three different people about this. And each one needs a completely different explanation.&lt;/p&gt;




&lt;h2&gt;
  
  
  For Your Engineering Team: Technical Precision
&lt;/h2&gt;

&lt;p&gt;Your team needs the &lt;em&gt;exact&lt;/em&gt; failure chain. This goes in your internal KB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INCIDENT: LMS Outage – Root Cause Analysis

TIMELINE:
- 14:32 UTC: DRM service returned 503 errors
- 14:45 UTC: LMS course playback failed for all users
- 15:10 UTC: Incident team identified DRM service logs showing upstream failures
- 15:30 UTC: Traced to [Third-Party Vendor] S3 storage degradation

ROOT CAUSE:
Videos stored in multiple undocumented locations:
- Primary: Company S3 bucket (monitored, failover)
- Undocumented: Vendor managed storage (no monitoring, no alerting)

IMPACT:
- 6-hour outage affecting 2,400 active learners
- ~$8K revenue loss
- 47 support escalations

RESOLUTION:
1. Consolidated all videos to primary S3 bucket
2. Deprovisioned vendor storage
3. Implemented CloudFront caching layer
4. Added automated inventory checks to CI/CD

PREVENTION:
- Infrastructure audit scheduled
- Documentation required for all storage decisions
- Monitoring alerts for upstream DRM health

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

&lt;/div&gt;



&lt;p&gt;This is detailed. Technical. It answers the "how" and "why" for people building the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  For Your Management Team: Business Impact
&lt;/h2&gt;

&lt;p&gt;Your non-technical stakeholders don't care about S3 buckets or DRM protocols. They care about impact and action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBJECT: LMS Outage – Summary &amp;amp; Next Steps

We experienced a 6-hour LMS outage this afternoon affecting ~2,400 students.

WHAT HAPPENED:
Our video storage system had videos in two places instead of one. When the
secondary location had problems, it broke course delivery because of how our
DRM is configured. This secondary location wasn't documented when originally set up.

BUSINESS IMPACT:
- 2,400 students unable to access courses (6 hours)
- ~$8K estimated revenue impact
- 47 customer support tickets

WHAT WE'RE DOING:
- Consolidated storage to a single monitored location
- Added safeguards against configuration drift
- Conducting full infrastructure review

TIMELINE:
All systems operational as of 8:45 PM. No further issues expected.

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

&lt;/div&gt;



&lt;p&gt;Short. Factual. Focused on impact and next steps. Zero jargon.&lt;/p&gt;




&lt;h2&gt;
  
  
  For Your Customers: Accountability
&lt;/h2&gt;

&lt;p&gt;Your customers don't care about your infrastructure. They care that their courses were down. Keep it brief, take responsibility, show you fixed it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBJECT: Service Restored – Course Access

We experienced a service interruption this afternoon (2:30 PM – 8:45 PM UTC)
that prevented course access.

WHAT HAPPENED:
A configuration in our video delivery system became misaligned, affecting
course playback. We've identified and resolved the root cause.

WHAT WE DID:
- Restored full service at 8:45 PM
- Consolidated video storage to prevent similar issues
- Added monitoring to catch problems faster

FOR YOUR ACCOUNT:
You have not been charged for downtime. Your course progress is intact.
Resume immediately.

Thank you for your patience.

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

&lt;/div&gt;



&lt;p&gt;Short. Accountable. Solution-focused. No technical noise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Skill
&lt;/h2&gt;

&lt;p&gt;You're dealing with the same incident. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your team&lt;/strong&gt; needs technical details to prevent it happening again&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your management&lt;/strong&gt; needs business impact to make resource decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your customers&lt;/strong&gt; need reassurance and accountability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Engineers who get promoted aren't always the ones who build the cleverest systems. They're the ones who can explain what they've built - and why it matters - to anyone in the room.&lt;/p&gt;

&lt;p&gt;That's not soft skill theater. That's infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Same incident, three audiences, three completely different explanations. Master that shift and you'll communicate like an engineer who actually ships things.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: communication · technical writing · stakeholder management · incident communication · career development · team leadership · infrastructure · documentation&lt;/em&gt;&lt;/p&gt;

</description>
      <category>communication</category>
      <category>technicalwriting</category>
      <category>stakeholdermanagemen</category>
      <category>incidentcommunicatio</category>
    </item>
    <item>
      <title>The State of Hiring, AI, and 2025</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sun, 19 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/the-state-of-hiring-ai-and-2025-11p0</link>
      <guid>https://dev.to/atharvaunde/the-state-of-hiring-ai-and-2025-11p0</guid>
      <description>&lt;p&gt;I'm a 2020 Computer Engineering graduate, part of the first batch that had to navigate the chaos of the COVID-19 pandemic during graduation. Even back then, I felt that the hiring process, both in India and globally, was outdated.&lt;/p&gt;

&lt;p&gt;Now, let me clarify. I don't hold anything against people who swear by DSA (Data Structures and Algorithms), competitive programming, or LeetCode-style assessments. What I'm sharing here is simply my perspective based on personal experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Journey: Not a DSA Kid
&lt;/h2&gt;

&lt;p&gt;I never got into DSA or competitive coding. Partly due to a lack of mentorship in college, and partly because I was more interested in getting my hands dirty with real technologies and actually building things.&lt;/p&gt;

&lt;p&gt;Back in the pre-ChatGPT era, Stack Overflow and Server Fault were our lifelines. We'd spend hours debugging an issue, desperately searching for the one solution that worked. That process, though tedious, taught me how to solve problems.&lt;/p&gt;

&lt;p&gt;When I started my career, I was rejected by a few companies in the final coding or tech rounds simply because my DSA wasn't "strong enough." Writing palindrome checkers or reversing strings never really excited me. My thought was - why obsess over that when the internet already has those answers? What matters is knowing how to apply solutions to real problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With the Current Hiring System
&lt;/h2&gt;

&lt;p&gt;Even today, the hiring process for tech roles remains fragmented and, honestly, quite outdated. I've been involved in hiring over the last few months and I've seen both sides of the equation.&lt;/p&gt;

&lt;p&gt;When I interview, I rarely ask DSA questions. For a Node.js backend developer role, for instance, I usually ask the candidate to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a simple Express server&lt;/li&gt;
&lt;li&gt;Connect it to MongoDB using Mongoose&lt;/li&gt;
&lt;li&gt;Implement basic CRUD operations&lt;/li&gt;
&lt;li&gt;Handle errors and edge cases and later start with Aggregation Pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They can refer to official docs or npm pages. Still, I've seen candidates struggle to even set up a basic Express server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Insight:&lt;/strong&gt; We're in &lt;strong&gt;2025&lt;/strong&gt; , in the age of ChatGPT, Copilot, and LLMs. Writing code has become easier than ever. What truly matters now isn't whether someone can memorize syntax, but &lt;strong&gt;whether they can think through problems and find the right solutions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Real Skills We Should Be Hiring For
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Problem-Solving Ability
&lt;/h3&gt;

&lt;p&gt;Anyone can write code with AI's help. The differentiator is how well a person can break down a problem, analyze trade-offs, and come up with a solution that makes sense.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Search Skills
&lt;/h3&gt;

&lt;p&gt;You'd be surprised how many developers can't simply "Google" effectively. I've seen juniors write full-paragraph queries into search boxes and get frustrated with irrelevant results. Searching or "prompting" now is a real skill.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Reading Documentation
&lt;/h3&gt;

&lt;p&gt;This is another lost art. I once sat with a junior developer (with 3 years of experience) who couldn't identify the accepted answer on a Stack Overflow page. It wasn't a knowledge problem - it was a &lt;em&gt;reading comprehension&lt;/em&gt; problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Modern, AI-Aware Hiring Process
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Post a job requirement&lt;/strong&gt; with a short take-home challenge for front-end, back-end, or DevOps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Give candidates time&lt;/strong&gt; to complete it using any tools or AI assistants they want.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In the first interview round,&lt;/strong&gt; ask them to run their code, make small changes live, and explain their thought process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discuss scalability and problem scenarios&lt;/strong&gt; - "What happens if traffic spikes?", "How would you handle data consistency?", etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review past projects&lt;/strong&gt; - see how they've thought through architecture, trade-offs, and delivery.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of spending endless hours on multi-round interviews, I'd even suggest hiring candidates on a &lt;strong&gt;trial basis for 15 days.&lt;/strong&gt; Give them a real problem to solve. See how they perform when faced with an actual requirement, like implementing a secure login system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What Separates Good Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Anyone can ask ChatGPT to generate a login page, but a skilled developer knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It needs authentication checks&lt;/li&gt;
&lt;li&gt;OAuth requires privacy policy and ToS links&lt;/li&gt;
&lt;li&gt;Idle sessions should time out&lt;/li&gt;
&lt;li&gt;Security headers must be configured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That level of awareness doesn't come from solving string reversals - it comes from &lt;em&gt;building real systems and understanding user needs.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It's Time to Rethink Hiring
&lt;/h2&gt;

&lt;p&gt;We're at a point where the ability to write code isn't the bottleneck anymore. AI has taken care of that. The real test is how people think, learn, and adapt their &lt;strong&gt;judgment.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If companies keep hiring like it's 2015, they'll miss out on great talent that can think creatively and work smartly with AI.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: AI hiring · recruitment trends 2025 · tech talent market · DevOps jobs · engineering hiring · AI in recruitment · developer hiring · talent market 2025 · career insights&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aihiring</category>
      <category>recruitmenttrends202</category>
      <category>techtalentmarket</category>
      <category>devopsjobs</category>
    </item>
    <item>
      <title>Docker: Layers, Caching, Multi-Stage Explained</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 15 Feb 2025 12:55:45 +0000</pubDate>
      <link>https://dev.to/atharvaunde/docker-layers-caching-multi-stage-explained-10b5</link>
      <guid>https://dev.to/atharvaunde/docker-layers-caching-multi-stage-explained-10b5</guid>
      <description>&lt;p&gt;Docker's efficiency is one of its biggest draws. But what makes Docker builds so fast? The secret lies in its layer-based architecture and clever caching mechanism. Let's dive in and see how it all works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfiles: A Layered Cake&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every line in your Dockerfile is an instruction, and Docker treats each of these instructions as a distinct &lt;em&gt;layer&lt;/em&gt;. But what is a layer, exactly?&lt;/p&gt;

&lt;p&gt;Think of it like this: a layer is an intermediate snapshot of your container image during the build process. Each instruction in the Dockerfile creates a new layer, building upon the previous one.&lt;/p&gt;

&lt;p&gt;For instance, consider this common Node.js Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple Dockerfile translates into five distinct layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Base Image Layer:&lt;/strong&gt; &lt;code&gt;FROM node:18&lt;/code&gt; (The foundation upon which everything else is built)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Working Directory Layer:&lt;/strong&gt; &lt;code&gt;WORKDIR /app&lt;/code&gt; (Sets the working directory inside the container)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Definition Layer:&lt;/strong&gt; &lt;code&gt;COPY package.json .&lt;/code&gt; (Copies the &lt;code&gt;package.json&lt;/code&gt; file)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Installation Layer:&lt;/strong&gt; &lt;code&gt;RUN npm install&lt;/code&gt; (Installs the project dependencies)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Application Code Layer:&lt;/strong&gt; &lt;code&gt;COPY . .&lt;/code&gt; (Copies the entire application code)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these instructions results in a distinct layer that's stored in the image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker's Caching Superpower&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's where the magic happens: Docker caches each of these layers during the build process. This means that if a layer hasn't changed, Docker can reuse the cached version instead of rebuilding it from scratch. This dramatically speeds up subsequent builds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Cache Hit:&lt;/strong&gt; If an instruction and its inputs haven't changed, Docker pulls the existing layer from the cache.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cache Miss:&lt;/strong&gt; If an instruction or its inputs &lt;em&gt;have&lt;/em&gt; changed, Docker invalidates the cache for that layer &lt;em&gt;and all subsequent layers&lt;/em&gt;. This means it needs to rebuild not only the changed layer but also every layer that comes after it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cache Invalidation: When Things Go Wrong&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cache invalidation behavior is crucial to understand. Imagine you have a Dockerfile with eight instructions. If instruction #2 changes, Docker invalidates the cache for instruction #2 &lt;em&gt;and all instructions that follow&lt;/em&gt; (3 through 8). They will all need to be rebuilt. This can lead to longer build times if not managed correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Real-World Example (Multi-Stage Build and Labels)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's examine a more complex scenario involving a multi-stage Dockerfile, which is a best practice for creating smaller and more secure images:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.authors="authoremail@example.com"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; "com.example.vendor"="Example LLC"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; version="1.0.0"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; description="This image is used to run hello world backend written in Express Framework"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this Dockerfile, we have two stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;build-env&lt;/code&gt; Stage:&lt;/strong&gt; This stage uses a Node.js Alpine image to install dependencies and prepare the application for production.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Final Stage:&lt;/strong&gt; This stage uses a distroless image (&lt;code&gt;gcr.io/distroless/nodejs20-debian12&lt;/code&gt;), which contains only the necessary runtime dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's how caching works in this multi-stage context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Independent Caches:&lt;/strong&gt; Each stage has its own separate cache. Changes in one stage don't automatically invalidate the cache of other stages, &lt;em&gt;unless&lt;/em&gt; they affect the &lt;code&gt;COPY --from&lt;/code&gt; instruction (which we'll discuss below).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;build-env&lt;/code&gt; Stage Changes:&lt;/strong&gt; If you modify &lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;yarn.lock&lt;/code&gt; in the &lt;code&gt;build-env&lt;/code&gt; stage, the &lt;code&gt;RUN yarn install&lt;/code&gt; instruction will be invalidated, and all subsequent instructions in &lt;em&gt;that stage&lt;/em&gt; will need to be rebuilt.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;COPY --from&lt;/code&gt; Interaction:&lt;/strong&gt; The &lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; instruction is crucial. If the &lt;em&gt;contents&lt;/em&gt; of &lt;code&gt;/app&lt;/code&gt; in the &lt;code&gt;build-env&lt;/code&gt; stage change (due to a rebuild triggered by a change in &lt;code&gt;package.json&lt;/code&gt;, for example), the &lt;code&gt;COPY&lt;/code&gt; instruction will also produce a different result in the final stage, invalidating the final stage's cache from that point onward.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Label Invalidation:&lt;/strong&gt; The &lt;code&gt;LABEL&lt;/code&gt; instructions, while important for adding metadata, &lt;em&gt;do not&lt;/em&gt; directly influence the caching mechanism. Changing label values will always cause the layer containing the &lt;code&gt;LABEL&lt;/code&gt; instruction to be rebuilt, but it doesn't impact any previous layers.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Code Changes:&lt;/strong&gt; If you simply modify code in the &lt;code&gt;index.js&lt;/code&gt; file, only the &lt;code&gt;COPY index.js ./&lt;/code&gt; instruction within &lt;code&gt;build-env&lt;/code&gt;, and the subsequent &lt;code&gt;COPY --from&lt;/code&gt; instruction in the final stage will be affected. The dependency installation stage (&lt;code&gt;RUN yarn install&lt;/code&gt;) can still be pulled from the cache, speeding up the build significantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Docker Caching and Multi-Stage Builds: Scenario Table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This table outlines how different changes to your Dockerfile or application code impact the caching mechanism in a multi-stage build.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Changed File/Instruction&lt;/th&gt;
&lt;th&gt;Impact on &lt;code&gt;build-env&lt;/code&gt; Stage Cache&lt;/th&gt;
&lt;th&gt;Impact on Final Stage Cache&lt;/th&gt;
&lt;th&gt;Rebuilt Layers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Change:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;yarn.lock&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RUN yarn install&lt;/code&gt; and subsequent instructions are invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; and subsequent instructions invalidated.&lt;/td&gt;
&lt;td&gt;All layers from &lt;code&gt;RUN yarn install&lt;/code&gt; in &lt;code&gt;build-env&lt;/code&gt;, and from &lt;code&gt;COPY --from&lt;/code&gt; in the final stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Change Only:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only &lt;code&gt;COPY index.js ./&lt;/code&gt; is invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; and subsequent instructions invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY index.js ./&lt;/code&gt; in &lt;code&gt;build-env&lt;/code&gt;, and from &lt;code&gt;COPY --from&lt;/code&gt; in the final stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dockerfile Change (build-env, Before COPY package.json)`:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(e.g., adding a new &lt;code&gt;ENV&lt;/code&gt; variable before COPY)&lt;/td&gt;
&lt;td&gt;All instructions after and including the changed instruction are invalidated.&lt;/td&gt;
&lt;td&gt;If the content of /app does not change, the final stage stays cached&lt;/td&gt;
&lt;td&gt;All layers from that step to end of &lt;code&gt;build-env&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dockerfile Change (build-env, After COPY package.json)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(e.g., adding an RUN after copy)&lt;/td&gt;
&lt;td&gt;All instructions after and including the changed instruction are invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; and subsequent instructions invalidated.&lt;/td&gt;
&lt;td&gt;All layers from changed instruction till end of &lt;code&gt;build-env&lt;/code&gt; and onwards.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Label Value Change:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(Change in LABEL instruction in the final stage)&lt;/td&gt;
&lt;td&gt;No impact.&lt;/td&gt;
&lt;td&gt;Only the layer with the modified &lt;code&gt;LABEL&lt;/code&gt; is invalidated.&lt;/td&gt;
&lt;td&gt;Layer containing the &lt;code&gt;LABEL&lt;/code&gt; instruction in the final stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No Changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;All layers are pulled from cache.&lt;/td&gt;
&lt;td&gt;All layers are pulled from cache.&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Scenario:&lt;/strong&gt; Describes the type of change made.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Changed File/Instruction:&lt;/strong&gt; Specifies the file or instruction that was modified.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Impact on &lt;code&gt;build-env&lt;/code&gt; Stage Cache:&lt;/strong&gt; Explains which layers in the &lt;code&gt;build-env&lt;/code&gt; stage are invalidated.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Impact on Final Stage Cache:&lt;/strong&gt; Explains which layers in the final stage are invalidated.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Rebuilt Layers:&lt;/strong&gt; Lists the layers that will be rebuilt during the Docker build process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Takeaway: Order and Multi-Stage Considerations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With multi-stage builds, you need to consider caching within &lt;em&gt;each&lt;/em&gt; stage, as well as how changes in one stage affect subsequent stages through &lt;code&gt;COPY --from&lt;/code&gt; instructions. Strategic placement of instructions and careful management of dependencies are key to maximizing build performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the next section, we will explore best practices to optimize caching and reduce unnecessary rebuilds. Stay tuned!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>jenkins</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Dockerfile Labels: A Comprehensive Guide</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 08 Feb 2025 08:02:10 +0000</pubDate>
      <link>https://dev.to/atharvaunde/dockerfile-labels-a-comprehensive-guide-11fd</link>
      <guid>https://dev.to/atharvaunde/dockerfile-labels-a-comprehensive-guide-11fd</guid>
      <description>&lt;p&gt;In our &lt;a href="https://dev.to/atharvaunde/base-images-the-secret-to-smaller-docker-images-43n3"&gt;previous post&lt;/a&gt;, we explored how to significantly optimize Docker image size. Now, let's dive deeper and enhance our images with valuable metadata using Docker labels.&lt;/p&gt;

&lt;p&gt;Remember this Dockerfile from last time?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we inspect this image and check its labels using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{json .Config.Labels}}'&lt;/span&gt; atharvaunde/dockerexamples:distroless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll see... &lt;code&gt;null&lt;/code&gt;. So, what's the point of Docker labels, and why should we care?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Use Docker Labels?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker labels are essentially metadata tags that you can add to your Docker images. Think of them as key-value pairs that provide extra information about the image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Cases for Docker Labels&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Attribution:&lt;/strong&gt; Who built this image? Include the author's name and email.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Organization:&lt;/strong&gt; Which company or team created this image?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Version Tracking:&lt;/strong&gt; What version of the application is contained within? This is especially useful if you consistently tag images as &lt;code&gt;latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Description:&lt;/strong&gt;  Provide a short description of the image's purpose.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Custom Metadata:&lt;/strong&gt; Add any other relevant information, such as dependencies, build dates, or license details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By embedding this information directly into the image, you provide valuable context to users who consume your images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding Labels to Our Dockerfile&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's enhance our Dockerfile with labels to include the author, company, version, and a description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.authors="authoremail@example.com"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; "com.example.vendor"="Example LLC"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; version="1.0.0"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; description="This image is used to run hello world backend written in Express Framework"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when we run the same inspection command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{json .Config.Labels}}'&lt;/span&gt; atharvaunde/dockerexamples:distroless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll see the labels we added:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"com.example.vendor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example LLC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This image is used to run hello world backend written in Express Framework"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"org.opencontainers.image.authors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authoremail@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&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;&lt;strong&gt;Important Placement in Multi-Stage Builds&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using multi-stage Dockerfiles like ours, it's crucial to place the &lt;code&gt;LABEL&lt;/code&gt; instructions in the &lt;em&gt;final&lt;/em&gt; stage (i.e., the one that creates the actual image you'll be distributing). If we had placed the &lt;code&gt;LABEL&lt;/code&gt; instructions &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;FROM gcr.io/distroless/nodejs20-debian12&lt;/code&gt; line, they would be lost in the final image. The final image is created in a separate stage with separate context, so it wouldn't inherit labels from any previous stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker Label Inheritance and Overriding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This table summarizes how labels are handled when a Dockerfile adds a label that either exists or doesn't exist in the base image.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Base Image Label&lt;/th&gt;
&lt;th&gt;Dockerfile Label&lt;/th&gt;
&lt;th&gt;Resulting Image Label&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Inheritance:&lt;/strong&gt; Label Exists in Base Image&lt;/td&gt;
&lt;td&gt;Exists&lt;/td&gt;
&lt;td&gt;Absent&lt;/td&gt;
&lt;td&gt;Retained (from base)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Overriding:&lt;/strong&gt; Label Exists in Base Image&lt;/td&gt;
&lt;td&gt;Exists&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;td&gt;Overridden (Dockerfile value)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Addition:&lt;/strong&gt; Label Not in Base Image&lt;/td&gt;
&lt;td&gt;Absent&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;td&gt;Retained (Dockerfile value)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;In Conclusion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker labels are a simple yet powerful way to add metadata to your Docker images, providing valuable context and improving discoverability. Remember to place your &lt;code&gt;LABEL&lt;/code&gt; instructions in the correct stage of a multi-stage Dockerfile and be aware of how label inheritance works. Happy containerizing!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Base Images: The Secret to Smaller Docker Images</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sun, 02 Feb 2025 12:30:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/base-images-the-secret-to-smaller-docker-images-43n3</link>
      <guid>https://dev.to/atharvaunde/base-images-the-secret-to-smaller-docker-images-43n3</guid>
      <description>&lt;p&gt;In our &lt;a href="https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24"&gt;previous post&lt;/a&gt;, we walked through creating a basic Dockerfile. However, we noticed a significant issue: the resulting image for our simple "Hello World" app was a hefty 1.62GB (uncompressed)! That’s not ideal for efficient deployment and resource utilization.&lt;/p&gt;

&lt;p&gt;Today, we're diving into how to dramatically reduce your Docker image size by carefully choosing the right base image. You might be surprised by how much impact this single decision can have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Impact of Your Base Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you build a Docker image, you're essentially layering instructions on top of a foundational image – the &lt;em&gt;base image&lt;/em&gt;. All your commands, dependencies, and application code get added on to this base. Consequently, the size and contents of your base image have a direct impact on the final size of your container image.&lt;/p&gt;

&lt;p&gt;As an example, in our previous attempt, using &lt;code&gt;FROM node:latest&lt;/code&gt; resulted in a 1.62GB (uncompressed) image. That's a lot of bloat for a tiny Node.js application! &lt;/p&gt;

&lt;p&gt;Just by switching our base image to &lt;code&gt;FROM node:alpine&lt;/code&gt;, we saw the size drop to 244MB (uncompressed). That's a huge improvement, but can we do better? Absolutely! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding Different Base Image Types&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's explore the common types of base images and when to consider using them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standard Images:&lt;/strong&gt; These are the full-fledged OS-based images like Ubuntu, Debian, or others. They come packed with a wide range of libraries and tools. While convenient, these images tend to be large due to all the extra baggage they carry. Unless you're unsure about your application's OS dependencies or are in a real rush, it's best to avoid them for production containers due to their size and resource consumption.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alpine Images:&lt;/strong&gt; These images are based on the super lightweight Alpine Linux distribution. They are much smaller than standard images because they contain only the bare minimum packages needed to run your application. They are ideal for most use-cases and are one of the best choices when starting out with Docker optimization. However, be sure to test your application thoroughly when first switching to Alpine images, as they might lack OS-level dependencies that your application unexpectedly relies on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Slim Images:&lt;/strong&gt; While the name might suggest otherwise, slim images can sometimes be larger than Alpine images, but still much smaller than standard ones. They often include only the packages and dependencies required to run specific applications, so it's worth exploring these images if their package set fits your use case. They can be based on various distributions like Alpine, CentOS, or Debian.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Distroless Images:&lt;/strong&gt; These are specially designed for multi-stage Docker builds. They contain &lt;em&gt;only&lt;/em&gt; your application and its runtime dependencies, excluding package managers, shells, and other common Linux utilities. This makes them incredibly small and helps improve the security posture of your containers. Distroless images are perfect for production deployments after you understand the entire application stack and dependencies. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As Google, the creator of distroless images, puts it, "Distroless images contain only your application and its runtime dependencies." You can read more about them &lt;a href="https://github.com/GoogleContainerTools/distroless" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Choosing the Right Image: A Practical Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deciding which image to use might seem daunting, but a simple approach helps here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Start with Alpine:&lt;/strong&gt; Begin with an Alpine-based image like &lt;code&gt;node:alpine&lt;/code&gt; for Node.js applications.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Inspect and Verify:&lt;/strong&gt; Examine the image's layers and included packages on Docker Hub. This can give you insights into the image's composition. For example, you can check the layers of a specific image tag like &lt;code&gt;node:current-alpine3.20&lt;/code&gt; &lt;a href="https://hub.docker.com/layers/library/node/current-alpine3.20/images/sha256-3a5020f72984cd15b5abc6e285843a19510ee6ec21eb523268811f0606e65ef9" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Add Dependencies Manually:&lt;/strong&gt; If an Alpine image lacks required dependencies, you can install them manually within your Dockerfile. You can even create your own custom base image from scratch if needed.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Advance to Distroless:&lt;/strong&gt; For production, after thorough testing, and when the full application dependencies are well-known, consider using distroless images for maximum size reduction and security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Let's Compare Image Sizes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To illustrate the point, let's take a look at the image sizes we saw in our example, using different base images. We'll show both uncompressed and compressed sizes for comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base Image &amp;amp; Build Setup&lt;/th&gt;
&lt;th&gt;Image Tag&lt;/th&gt;
&lt;th&gt;Uncompressed Docker Image&lt;/th&gt;
&lt;th&gt;Compressed Docker Image&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;node:20-alpine&lt;/code&gt; (build) &amp;amp; &lt;code&gt;gcr.io/distroless/nodejs20-debian12&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:distroless&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;191 MB&lt;/td&gt;
&lt;td&gt;49.61 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;364 MB&lt;/td&gt;
&lt;td&gt;78.52 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;244 MB&lt;/td&gt;
&lt;td&gt;56.68 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.62 GB&lt;/td&gt;
&lt;td&gt;381.73 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: The uncompressed size can be checked by using the command &lt;code&gt;docker image ls | grep mycontainer&lt;/code&gt;. The compressed size can be seen when the image is pushed to a container registry or by using &lt;code&gt;docker save mycontainer:distroless | gzip -c | wc -c&lt;/code&gt; to see the compressed file size in bytes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/atharvaunde/understanding-the-difference-compressed-vs-uncompressed-docker-image-sizes-1fn0"&gt;Whats the difference in compressed and uncompressed size?&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Optimization through Distroless (Multi-stage Build)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's see how to optimize it even further using a Distroless image. Here's an updated Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We use &lt;code&gt;node:20-alpine&lt;/code&gt; as a &lt;em&gt;builder&lt;/em&gt; image to install our dependencies, copy the source, and prepare the application for production.&lt;/li&gt;
&lt;li&gt; We then copy all necessary artifacts (&lt;code&gt;/app&lt;/code&gt;) into a distroless image &lt;code&gt;gcr.io/distroless/nodejs20-debian12&lt;/code&gt; which only contains the absolute runtime requirements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This method creates a final image with a minimal footprint, as demonstrated in the table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choosing the right base image is a critical step in optimizing your Docker images. Start with Alpine images, manually add needed dependencies, and ultimately aim for distroless images in production. By understanding the trade-offs of each image type, you can greatly reduce your image size, improving efficiency, performance, and resource consumption. Stay tuned for more Docker tips and tricks in future blog posts!&lt;/p&gt;

&lt;p&gt;The table clearly highlights the dramatic size difference when using different base images and build strategies. The multi-stage distroless approach yields the smallest final image, making it ideal for production deployments.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understanding the Difference: Compressed vs. Uncompressed Docker Image Sizes</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 01 Feb 2025 13:40:36 +0000</pubDate>
      <link>https://dev.to/atharvaunde/understanding-the-difference-compressed-vs-uncompressed-docker-image-sizes-1fn0</link>
      <guid>https://dev.to/atharvaunde/understanding-the-difference-compressed-vs-uncompressed-docker-image-sizes-1fn0</guid>
      <description>&lt;p&gt;When you build a Docker image, you're essentially creating a series of layers, each representing a step or instruction in your Dockerfile. These layers build on top of each other, forming the final image. Let's break down what the compressed and uncompressed sizes mean in this context:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uncompressed Image Size&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;What it is:&lt;/strong&gt; The uncompressed size represents the &lt;em&gt;total&lt;/em&gt; size of all the layers in your Docker image as they exist on your local machine. This includes all the intermediate layers created during the build process, as well as any duplicate data that may exist across layers. Think of it as the raw, unoptimized size of your image.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Why it's larger:&lt;/strong&gt; This size is usually much larger because:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Intermediate Layers:&lt;/strong&gt; Docker caches intermediate layers during the build process. These layers are kept for efficiency if you rebuild the image later, but they all contribute to the total uncompressed size.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Duplicate Data:&lt;/strong&gt; When you copy files into your image, Docker might create new layers even if some data is repeated from previous layers. For example, if you copy a directory in an early step and copy it again later with minor changes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Unoptimized Storage:&lt;/strong&gt;  The uncompressed size doesn't take advantage of compression or any optimized storage techniques.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Compressed Image Size&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;What it is:&lt;/strong&gt; The compressed size is the size of your Docker image after it has been processed and optimized by Docker for storage and transfer. This is the size you'll see when the image is pushed to a registry like Docker Hub or when you manually save it using &lt;code&gt;docker save&lt;/code&gt; with compression.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;How it's smaller:&lt;/strong&gt; Docker applies several optimizations during this process:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Gzip Compression:&lt;/strong&gt; The most significant optimization is that Docker uses gzip to compress the individual layers of your image. This significantly reduces the amount of storage space required.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Layer Deduplication:&lt;/strong&gt; Docker identifies and removes duplicate data across layers. If two layers contain the same file, only one copy of the data will be stored, and layers can reference that single source.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Optimized Storage:&lt;/strong&gt; Docker takes advantage of optimized storage mechanisms which only takes up space required for differences between layers and ensures it is stored efficiently.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Still Confused?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you're packing for an outing!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Uncompressed:&lt;/strong&gt; This is like throwing all your clothes, toiletries, and shoes into a big suitcase without any organization. It takes up a lot of space.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Compressed:&lt;/strong&gt; This is like carefully rolling up your clothes, using packing cubes to organize items, and removing any unnecessary duplicate items. The whole suitcase is now much smaller.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TL DR;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Local vs. Registry:&lt;/strong&gt; The uncompressed size is what you see locally while the compressed size is what's stored and transferred to/from a container registry.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Transfer Efficiency:&lt;/strong&gt; Docker optimizes images during upload to a container registry, and because of compression, downloads will also take less time.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;True Size:&lt;/strong&gt; The compressed size gives you a more accurate idea of the actual space your image will occupy on a registry or when you save it as a compressed archive.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Size Optimization Goal:&lt;/strong&gt; When trying to optimize your docker image size, the goal is to minimize the compressed size, because that affects download and upload times, and the cost of storage in the registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In Summary:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The uncompressed size is a useful metric to observe the effects of your changes during the image building process. However, the compressed size is the true indicator of how large your image is when stored and transferred. It's this compressed size that you should focus on when optimizing your Docker images for better performance and efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So how to check the size?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can check the uncompressed size by running &lt;code&gt;docker image ls | grep &amp;lt;your_image_name&amp;gt;&lt;/code&gt; command. To check the compressed size you can either push it to DockerHub, or use command &lt;code&gt;docker save &amp;lt;your_image_name&amp;gt; | gzip -c | wc -c&lt;/code&gt; to see compressed size in bytes.&lt;/p&gt;

&lt;p&gt;Still confused or having doubts?&lt;br&gt;
Write down in comments section!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>containers</category>
    </item>
    <item>
      <title>Excited to share my first blog post: "Practical Docker: Step-by-Step Container Creation and Execution"! In this guide, I walk through the essential steps of building and running a simple Docker container, with a focus on practical commands and concepts.</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 01 Feb 2025 11:27:11 +0000</pubDate>
      <link>https://dev.to/atharvaunde/excited-to-share-my-first-blog-post-practical-docker-step-by-step-container-creation-and-jh2</link>
      <guid>https://dev.to/atharvaunde/excited-to-share-my-first-blog-post-practical-docker-step-by-step-container-creation-and-jh2</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/atharvaunde" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2762428%2F8e921033-436b-4e3a-ae71-db308ce9af74.jpg" alt="atharvaunde"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Practical Docker: Step-by-Step Container Creation and Execution&lt;/h2&gt;
      &lt;h3&gt;Atharva Unde ・ Feb 1&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dockerforbeginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>devops</category>
      <category>docker</category>
      <category>dockerforbeginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Practical Docker: Step-by-Step Container Creation and Execution</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 01 Feb 2025 11:18:30 +0000</pubDate>
      <link>https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24</link>
      <guid>https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24</guid>
      <description>&lt;p&gt;Running your software in containers, whether it's a website, an app, or even your database, is becoming increasingly important. Docker is a powerful tool for this, and a crucial part of it is the Dockerfile.&lt;/p&gt;

&lt;p&gt;Think of a Dockerfile as a recipe for building a Docker image. It's a simple text file that lists all the steps needed to create a complete, self-contained package (the image) of your application. This includes things like installing necessary software, setting up your environment, and copying over your code. Docker reads this recipe and automatically builds the image, making sure everything is prepared exactly the way you want it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why all this effort?&lt;/strong&gt; &lt;br&gt;
Repeatable results are critical in DevOps. A Dockerfile ensures that every time you build your image, you get the exact same, reliable outcome. &lt;br&gt;
It eliminates the &lt;strong&gt;"it works on my machine"&lt;/strong&gt; problem by providing a consistent environment.&lt;/p&gt;

&lt;p&gt;Let's get started with the technical details of creating a Dockerfile. This will give you the foundation for building your own containerized applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:latest
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;u&gt;This example showcases a fundamental Dockerfile structure. In future articles, we'll explore more techniques for writing Dockerfiles that improve efficiency, security, and maintainability.&lt;br&gt;
&lt;/u&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Breakdown of the Dockerfile
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;FROM node:latest&lt;/strong&gt;&lt;br&gt;
This specifies the base image to use. node:latest pulls the latest official Node.js image from Docker Hub. This image already has Node.js and npm pre-installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WORKDIR /app&lt;/strong&gt;&lt;br&gt;
This sets the working directory inside the container to /app. This is crucial for organization and makes the Dockerfile more readable. Subsequent instructions will operate within this directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;COPY package*.json ./&lt;/strong&gt;&lt;br&gt;
This copies the package.json and package-lock.json files from the build context (your local project directory) into the /app directory inside the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RUN yarn&lt;/strong&gt;&lt;br&gt;
This crucial step runs the yarn command inside the container. It installs all the dependencies listed in package.json. This ensures the container has all the necessary packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;COPY . .&lt;/strong&gt;&lt;br&gt;
This copies all the remaining files and directories from the build context to the /app directory. This effectively copies the entire application code to the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EXPOSE 3000&lt;/strong&gt;&lt;br&gt;
This declares that the container will listen on port 3000. While it's important for visibility, it does not automatically map this port to your host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CMD ["node", "index.js"]&lt;/strong&gt;&lt;br&gt;
This is the command that runs when the container starts. It executes the Node.js file index.js. This is how your application is launched within the container. &lt;u&gt;There can be only be one CMD instruction in a Dockerfile.&lt;br&gt;
&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Now that we've created the Dockerfile, let's build the actual container image. This involves using the docker build command.&lt;/p&gt;

&lt;p&gt;To build your image, open your terminal and navigate to the directory containing your Dockerfile and the application code. Then, run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t mycontainer:latest .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This builds a docker image with the name as &lt;code&gt;myContainer&lt;/code&gt; and gets tagged as &lt;code&gt;latest&lt;/code&gt;. &lt;code&gt;.&lt;/code&gt; denotes that &lt;code&gt;Dockerfile&lt;/code&gt; is supposed to take the current directory from which the command is being run as the build context for source code and other files.&lt;/p&gt;

&lt;p&gt;If the build process is successful, you'll see output showing the image ID. You can verify the image was built by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
mycontainer   latest    e50c98928825   7 seconds ago   1.62GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's worry about the size of the image in the next chapter of the blog, where we will discuss how to write an optimized Dockerfile&lt;/p&gt;

&lt;p&gt;Lets run the image and see if the container is responding to our requests on &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 3000:3000 --name myapp mycontainer:latest 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a detached container named &lt;code&gt;myapp&lt;/code&gt; from the &lt;code&gt;mycontainer&lt;/code&gt; image using the &lt;code&gt;latest&lt;/code&gt; tag, maps port 3000 on the host to port 3000 inside the container, and starts the application defined in your CMD instruction inside the container. &lt;/p&gt;

&lt;p&gt;After running this command, you can access your application by navigating to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; in your browser on the host machine!&lt;/p&gt;

&lt;p&gt;Source Code: &lt;a href="https://github.com/atharvaunde/dockerExamples.git" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do let me know in comments in case you face any issues,&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>dockerforbeginners</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
