<?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>Forem: Ilyas Rufai</title>
    <description>The latest articles on Forem by Ilyas Rufai (@rufilboss).</description>
    <link>https://forem.com/rufilboss</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F548489%2F0921b258-f827-4615-a9cf-d5c6f016875b.jpg</url>
      <title>Forem: Ilyas Rufai</title>
      <link>https://forem.com/rufilboss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rufilboss"/>
    <language>en</language>
    <item>
      <title>Amazon S3 Bucket Names Aren’t Always Globally Unique Anymore — Here’s What Changed (and Why I’m Excited)</title>
      <dc:creator>Ilyas Rufai</dc:creator>
      <pubDate>Thu, 19 Mar 2026 11:54:35 +0000</pubDate>
      <link>https://forem.com/rufilboss/amazon-s3-bucket-names-arent-always-globally-unique-anymore-heres-what-changed-and-why-im-3a1f</link>
      <guid>https://forem.com/rufilboss/amazon-s3-bucket-names-arent-always-globally-unique-anymore-heres-what-changed-and-why-im-3a1f</guid>
      <description>&lt;p&gt;&lt;a href="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%2Farticles%2F74tcw6c7amqslpu9mfz6.png" class="article-body-image-wrapper"&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%2Farticles%2F74tcw6c7amqslpu9mfz6.png" alt="Global namespace" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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%2Farticles%2Fgj70v2rg5nh1b86zsuyc.png" alt="Account regional namespace" width="800" height="349"&gt;
&lt;/h2&gt;

&lt;p&gt;I still remember the first time S3 humbled me.&lt;/p&gt;

&lt;p&gt;I was following a “hello AWS” tutorial, feeling confident, and then S3 hit me with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bucket name already exists&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I tried again. And again. And again.&lt;br&gt;&lt;br&gt;
Different names, different variations, still taken!&lt;/p&gt;

&lt;p&gt;That’s when I learned the “classic rule”:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 bucket names are globally unique&lt;/strong&gt; (within an AWS partition).&lt;br&gt;&lt;br&gt;
Meaning: if someone anywhere already owns that bucket name, you can’t have it.&lt;/p&gt;

&lt;p&gt;But here’s the big update I recently learned, and it’s &lt;em&gt;huge&lt;/em&gt; for builders, platform teams, and security folks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3 now supports creating general-purpose buckets inside an “account regional namespace”, which removes the “find a globally unique name” pain.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS announced this on &lt;strong&gt;Mar 12, 2026&lt;/strong&gt;:&lt;br&gt;
&lt;code&gt;https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-s3-account-regional-namespaces/&lt;/code&gt;&lt;/p&gt;



&lt;p&gt;In this article, I’ll break down what changed, how it works, why it matters, and how I’d explain it if I were designing bucket naming at scale.&lt;/p&gt;
&lt;h2&gt;
  
  
  What used to be true (and is still true by default)
&lt;/h2&gt;

&lt;p&gt;By default, &lt;strong&gt;general purpose buckets exist in a global namespace&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AWS puts it plainly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;“Each bucket name must be unique across all AWS accounts in all the AWS Regions within a partition.”&lt;/strong&gt;
Official docs: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;my-company-logs&lt;/code&gt; can only exist once (in that partition).&lt;/li&gt;
&lt;li&gt;If the bucket is deleted, the name becomes available again — and someone else could recreate it later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fcwy76sxvn8ixkt227rjv.png" class="article-body-image-wrapper"&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%2Farticles%2Fcwy76sxvn8ixkt227rjv.png" alt="Bucket already exists" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What changed: Account regional namespaces (Mar 2026)
&lt;/h2&gt;

&lt;p&gt;AWS introduced a second option:&lt;/p&gt;

&lt;p&gt;You can now create a &lt;strong&gt;general purpose bucket&lt;/strong&gt; in a namespace reserved only for your account:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“You can also choose to create a bucket in your account regional namespace. Your account regional namespace is a subdivision of the global namespace that only your account can create buckets in.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Official docs: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AWS also summarizes the “why” in the announcement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eliminates the need to find globally unique bucket names
&lt;/li&gt;
&lt;li&gt;makes it easier to build workloads like “one bucket per customer/team/dataset”
&lt;/li&gt;
&lt;li&gt;can be enforced using IAM policies/service control policies (SCPs)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Announcement: &lt;code&gt;https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-s3-account-regional-namespaces/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The nuance I want to keep clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Global namespace is still the default&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;But now you have a &lt;strong&gt;reserved&lt;/strong&gt; namespace you can opt into&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How the naming looks (the new naming convention)
&lt;/h2&gt;

&lt;p&gt;Buckets in your &lt;strong&gt;account regional namespace&lt;/strong&gt; follow this naming pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bucket-name-prefix-accountId-region-an
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Official example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amzn-s3-demo-bucket-111122223333-us-west-2-an
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So instead of fighting to get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;my-company-logs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…you can choose a predictable prefix like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;my-company-logs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and the final name will include your account + region suffix that makes it unique &lt;em&gt;to you&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Ftb18tnn2rnv6102zmjs1.png" class="article-body-image-wrapper"&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%2Farticles%2Ftb18tnn2rnv6102zmjs1.png" alt=" " width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters (in real systems)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) It kills the “I spent 20 minutes naming a bucket” problem
&lt;/h3&gt;

&lt;p&gt;If you’ve ever created buckets for different environments, you know this loop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;myapp-prod-logs&lt;/code&gt; (taken)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;myapp-prod-logs-1&lt;/code&gt; (taken)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;myapp-prod-logs-2026&lt;/code&gt; (taken)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;myapp-prod-logs-&amp;lt;random&amp;gt;&lt;/code&gt; (finally works, but now it’s ugly)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Account regional namespaces support a much more predictable naming scheme.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) It reduces risk after deletion (a security win)
&lt;/h3&gt;

&lt;p&gt;In the shared global namespace, once a bucket is deleted, the name becomes available again. Another account can recreate the name later and potentially receive requests intended for that name.&lt;/p&gt;

&lt;p&gt;Official docs: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In contrast, for account regional namespace buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only your account can create buckets in that namespace&lt;/li&gt;
&lt;li&gt;Another account cannot recreate your account-regional bucket name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Official docs: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3) It scales for bucket-per-customer / bucket-per-team designs
&lt;/h3&gt;

&lt;p&gt;AWS explicitly calls out bucket-per-customer/team/dataset workloads in the announcement:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-s3-account-regional-namespaces/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That’s a very modern SaaS/platform pattern, and it’s nice to see S3 making it easier to do it safely and predictably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an account-regional namespace bucket (AWS CLI)
&lt;/h2&gt;

&lt;p&gt;The official docs show creating a bucket using the AWS CLI like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api create-bucket &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--bucket&lt;/span&gt; amzn-s3-demo-bucket-012345678910-us-west-1-an &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--bucket-namespace&lt;/span&gt; account-regional &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--create-bucket-configuration&lt;/span&gt; &lt;span class="nv"&gt;LocationConstraint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-west-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Before you run the command, make sure your &lt;code&gt;aws cli&lt;/code&gt; command is updated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How I’d enforce this in an organization (IAM/SCP policy)
&lt;/h2&gt;

&lt;p&gt;If I were working on a platform team, I’d want consistent naming and fewer security foot-guns. AWS provides a clean enforcement pattern using the condition key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s3:x-amz-bucket-namespace&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an example IAM policy from the docs that denies &lt;code&gt;s3:CreateBucket&lt;/code&gt; unless the request is using &lt;code&gt;account-regional&lt;/code&gt;:&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;"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;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RequireAccountRegionalBucketCreation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:CreateBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringNotEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"s3:x-amz-bucket-namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"account-regional"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bucket naming rules (still important)
&lt;/h2&gt;

&lt;p&gt;Even with namespaces, S3 bucket names still follow the general naming rules, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3–63 characters
&lt;/li&gt;
&lt;li&gt;lowercase letters, numbers, periods (&lt;code&gt;.&lt;/code&gt;), and hyphens (&lt;code&gt;-&lt;/code&gt;) only
&lt;/li&gt;
&lt;li&gt;must start and end with a letter or number
&lt;/li&gt;
&lt;li&gt;no two adjacent periods
&lt;/li&gt;
&lt;li&gt;cannot look like an IP address
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Official rules: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Also note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-an&lt;/code&gt; is reserved for account regional namespace buckets
&lt;/li&gt;
&lt;li&gt;the account/regional suffix counts toward the 63-character limit
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Official docs: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model I use
&lt;/h2&gt;

&lt;p&gt;This is how I explain it to myself:&lt;/p&gt;

&lt;h3&gt;
  
  
  Global namespace (default)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Like a global username system
&lt;/li&gt;
&lt;li&gt;If someone owns it, you can’t use it
&lt;/li&gt;
&lt;li&gt;If deleted, someone else might grab it later
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Account regional namespace (new)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Like a reserved “username space” under &lt;em&gt;my account + my region&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;I still choose the prefix, but uniqueness is guaranteed by the suffix
&lt;/li&gt;
&lt;li&gt;Another account can’t recreate my account-regional bucket name
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;When I first learned S3 bucket names had to be globally unique, I accepted it as “just an AWS thing.”&lt;/p&gt;

&lt;p&gt;But account regional namespaces feel like AWS responding to how people actually build today: lots of environments, lots of teams, and a need for predictable and secure naming.&lt;/p&gt;

&lt;p&gt;If I’m building something new, I’d strongly consider defaulting new buckets to the account regional namespace — especially if naming conventions matter, or if I want to reduce the risk of name reuse after deletion.&lt;/p&gt;




&lt;h2&gt;
  
  
  References (official)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AWS announcement (Mar 12, 2026): &lt;code&gt;https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-s3-account-regional-namespaces/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Namespaces for general purpose buckets: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;General purpose bucket naming rules: &lt;code&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>s3</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>I built a zero-cost end-to-end DevOps pipeline (GitHub Actions + Docker + Kubernetes + Docker Hub)</title>
      <dc:creator>Ilyas Rufai</dc:creator>
      <pubDate>Wed, 04 Mar 2026 11:27:47 +0000</pubDate>
      <link>https://forem.com/rufilboss/i-built-a-zero-cost-end-to-end-devops-pipeline-github-actions-docker-kubernetes-docker-hub-2g9n</link>
      <guid>https://forem.com/rufilboss/i-built-a-zero-cost-end-to-end-devops-pipeline-github-actions-docker-kubernetes-docker-hub-2g9n</guid>
      <description>&lt;p&gt;I just finished a small but &lt;strong&gt;real&lt;/strong&gt; DevOps project and I want to share it in case you’re trying to build your own portfolio.&lt;/p&gt;

&lt;p&gt;The idea was simple: &lt;strong&gt;take a tiny app and wire the whole path from &lt;code&gt;git push&lt;/code&gt; → CI/CD → container registry → Kubernetes&lt;/strong&gt;, without paying for any cloud resources.&lt;/p&gt;

&lt;p&gt;You can grab the code here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub repo&lt;/strong&gt;: &lt;code&gt;https://github.com/rufilboss/devops-e2e-pipeline&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Hub image&lt;/strong&gt;: &lt;code&gt;docker.io/asruf/demo-app:latest&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I built (high level)
&lt;/h2&gt;

&lt;p&gt;Concretely, the project contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App&lt;/strong&gt;: Tiny Flask API (&lt;code&gt;app/main.py&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container&lt;/strong&gt;: Dockerfile (&lt;code&gt;app/Dockerfile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD&lt;/strong&gt;: GitHub Actions workflow that builds and pushes images to &lt;strong&gt;Docker Hub&lt;/strong&gt; (&lt;code&gt;.github/workflows/ci-cd.yaml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt;: &lt;code&gt;Deployment&lt;/code&gt; + &lt;code&gt;Service&lt;/code&gt; (&lt;code&gt;k8s/*.yaml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform (optional)&lt;/strong&gt;: creates the Kubernetes namespace (&lt;code&gt;terraform/*.tf&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything here runs &lt;strong&gt;for free&lt;/strong&gt; on a local cluster (kind or minikube) and a free Docker Hub + GitHub account.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites I used
&lt;/h2&gt;

&lt;p&gt;To follow exactly what I did, you’ll want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git + GitHub repo&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kubectl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;One local Kubernetes option:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kind&lt;/strong&gt; (what I used), or&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;minikube&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Terraform (optional, only for the IaC part)&lt;/li&gt;

&lt;li&gt;A &lt;strong&gt;Docker Hub&lt;/strong&gt; account (I used mine &lt;code&gt;asruf&lt;/code&gt;)&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project layout
&lt;/h2&gt;

&lt;p&gt;This is the layout of the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;devops-e2e-pipeline/
├── app
│   ├── Dockerfile
│   ├── main.py
│   └── requirements.txt
├── k8s
│   ├── namespace.yaml
│   └── deployment.yaml
├── terraform
│   ├── main.tf
│   └── k8s.tf
└── .github
    └── workflows
        └── ci-cd.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1) The app I used (simple Flask service)
&lt;/h2&gt;

&lt;p&gt;I deliberately kept the app tiny so the focus is on the &lt;strong&gt;pipeline&lt;/strong&gt;, not the code.&lt;/p&gt;

&lt;p&gt;It exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; — info about the service (name, version, env, status)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/health&lt;/code&gt; — liveness&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/ready&lt;/code&gt; — readiness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;app/main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APP_VERSION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ENV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ENV&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ready&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ready&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dependencies:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app/requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flask&amp;gt;=3.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app listens on &lt;strong&gt;port 8080&lt;/strong&gt;, which I re-use everywhere (Docker, Kubernetes, port-forward, etc.).&lt;/p&gt;




&lt;h2&gt;
  
  
  2) Containerizing it with Docker
&lt;/h2&gt;

&lt;p&gt;My Dockerfile is intentionally straightforward but shows some basic good practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slim base image&lt;/li&gt;
&lt;li&gt;Non-root user&lt;/li&gt;
&lt;li&gt;Requirements installed in their own layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;app/Dockerfile&lt;/code&gt;:&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;python:3.12-slim&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;runtime&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;RUN &lt;/span&gt;adduser &lt;span class="nt"&gt;--disabled-password&lt;/span&gt; &lt;span class="nt"&gt;--gecos&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; appuser

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.lock

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; main.py .&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; FLASK_APP=main.py&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "-m", "flask", "run", "--host=0.0.0.0", "--port=8080"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Local sanity check
&lt;/h3&gt;

&lt;p&gt;From the repo root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;devops-e2e-pipeline

docker build &lt;span class="nt"&gt;-t&lt;/span&gt; demo-app:local ./app
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;--name&lt;/span&gt; demo-app-test demo-app:local

&lt;span class="c"&gt;# In another terminal:&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8080/health
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gave me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{"status": "healthy"}&lt;/code&gt; from &lt;code&gt;/health&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{"env":"dev","service":"demo-app","status":"ok","version":"1.0.0"}&lt;/code&gt; from &lt;code&gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once that worked, I moved on to Kubernetes.&lt;/p&gt;

&lt;p&gt;Here are the screenshots from my terminal while doing this:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fdz7asuyn8phgm8tz7piu.png" class="article-body-image-wrapper"&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%2Farticles%2Fdz7asuyn8phgm8tz7piu.png" alt="Building the Docker image" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fm6w172zyapvk1uyibduu.png" class="article-body-image-wrapper"&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%2Farticles%2Fm6w172zyapvk1uyibduu.png" alt="Running the container locally" width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fi0uuxyouuk346jcxvb9a.png" class="article-body-image-wrapper"&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%2Farticles%2Fi0uuxyouuk346jcxvb9a.png" alt="Health endpoint response" width="800" height="25"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Flx7fl0934mpl26gn41mc.png" class="article-body-image-wrapper"&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%2Farticles%2Flx7fl0934mpl26gn41mc.png" alt="Root endpoint response" width="800" height="25"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3) Running it on Kubernetes (kind or minikube)
&lt;/h2&gt;

&lt;p&gt;I wanted a “real” deployment with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dedicated namespace&lt;/li&gt;
&lt;li&gt;2 replicas&lt;/li&gt;
&lt;li&gt;Liveness/readiness probes&lt;/li&gt;
&lt;li&gt;Resource requests/limits&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Starting a local cluster
&lt;/h3&gt;

&lt;p&gt;You can use either tool; I used &lt;strong&gt;kind&lt;/strong&gt;, but here are both options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;minikube:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;kind:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kind create cluster &lt;span class="nt"&gt;--name&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what that looked like for me:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fvdnjafp0jk4dxf1mmys8.png" class="article-body-image-wrapper"&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%2Farticles%2Fvdnjafp0jk4dxf1mmys8.png" alt="kind create cluster output" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Making the image visible to the cluster
&lt;/h3&gt;

&lt;p&gt;Kubernetes can’t automatically see &lt;code&gt;demo-app:local&lt;/code&gt; unless you either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build inside the cluster’s Docker daemon (minikube), or&lt;/li&gt;
&lt;li&gt;load the image into kind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Option A: minikube&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;minikube docker-env&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; demo-app:local ./app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B: kind&lt;/strong&gt; (what I used):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; demo-app:local ./app
kind load docker-image demo-app:local &lt;span class="nt"&gt;--name&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;kind load&lt;/code&gt; output:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fpdbfv6dlehglwfn6cqo6.png" class="article-body-image-wrapper"&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%2Farticles%2Fpdbfv6dlehglwfn6cqo6.png" alt="kind load docker-image output" width="800" height="25"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubernetes manifests I used
&lt;/h3&gt;

&lt;p&gt;Namespace:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;k8s/namespace.yaml&lt;/code&gt;:&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="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;Namespace&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;demo-app&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deployment + Service:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;k8s/deployment.yaml&lt;/code&gt;:&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/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;Deployment&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;demo-app&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;demo-app&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&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;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&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&lt;/span&gt;
          &lt;span class="c1"&gt;# Local image for kind/minikube:&lt;/span&gt;
          &lt;span class="c1"&gt;# image: demo-app:local&lt;/span&gt;
          &lt;span class="c1"&gt;# Docker Hub image (asruf/demo-app) when using CI/CD:&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.io/asruf/demo-app:latest&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&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;ENV&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production"&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_VERSION&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0"&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50m&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;64Mi&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;200m&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;128Mi&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/ready&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="nn"&gt;---&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;Service&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;demo-app&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;demo-app&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;http&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Applying and testing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/namespace.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s/deployment.yaml

kubectl get pods,svc &lt;span class="nt"&gt;-n&lt;/span&gt; demo-app

kubectl port-forward &lt;span class="nt"&gt;-n&lt;/span&gt; demo-app svc/demo-app 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8080/health
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8080/ready
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the &lt;code&gt;kubectl apply&lt;/code&gt; / &lt;code&gt;kubectl get&lt;/code&gt; snapshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fkp1it3q5vhwkqt8b42ri.png" class="article-body-image-wrapper"&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%2Farticles%2Fkp1it3q5vhwkqt8b42ri.png" alt="kubectl apply/get output" width="800" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the port-forward:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F57pxhpz1syi1jw50frhy.png" class="article-body-image-wrapper"&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%2Farticles%2F57pxhpz1syi1jw50frhy.png" alt="kubectl port-forward output" width="800" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, I had the app running as &lt;strong&gt;2 replicas in a local cluster&lt;/strong&gt;, fronted by a Service, with working probes.&lt;/p&gt;




&lt;h2&gt;
  
  
  4) Pushing to Docker Hub
&lt;/h2&gt;

&lt;p&gt;My Docker Hub username is &lt;strong&gt;&lt;code&gt;asruf&lt;/code&gt;&lt;/strong&gt;. I first pushed manually to make sure everything worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag demo-app:local asruf/demo-app:latest
docker push asruf/demo-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, the image was available at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker.io/asruf/demo-app:latest&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the image the Kubernetes manifest uses by default in this repo.&lt;/p&gt;




&lt;h2&gt;
  
  
  5) CI/CD with GitHub Actions → Docker Hub
&lt;/h2&gt;

&lt;p&gt;I wanted the pipeline to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build the image on every push / PR&lt;/li&gt;
&lt;li&gt;Push to Docker Hub on pushes (not PRs)&lt;/li&gt;
&lt;li&gt;Tag images with:

&lt;ul&gt;
&lt;li&gt;the commit SHA&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;latest&lt;/code&gt; (for the default branch)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The workflow is at &lt;code&gt;./.github/workflows/ci-cd.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Hub secrets
&lt;/h3&gt;

&lt;p&gt;In my GitHub repo I created 2 &lt;strong&gt;Actions secrets&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DOCKERHUB_USERNAME&lt;/code&gt; — &lt;code&gt;asruf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOCKERHUB_TOKEN&lt;/code&gt; — a Docker Hub access token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find these in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GitHub repo → Settings → Secrets and variables → Actions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What the workflow does
&lt;/h3&gt;

&lt;p&gt;High level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check out code&lt;/li&gt;
&lt;li&gt;Set up Buildx&lt;/li&gt;
&lt;li&gt;Log in to Docker Hub with &lt;code&gt;DOCKERHUB_USERNAME&lt;/code&gt; + &lt;code&gt;DOCKERHUB_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Build the Docker image from &lt;code&gt;./app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tag it with SHA + &lt;code&gt;latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Push to &lt;code&gt;docker.io/asruf/demo-app&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So every push to &lt;code&gt;main&lt;/code&gt; automatically gives me a fresh image on Docker Hub, ready for Kubernetes.&lt;/p&gt;




&lt;h2&gt;
  
  
  6) Optional: Terraform for the namespace
&lt;/h2&gt;

&lt;p&gt;I also wanted at least one &lt;strong&gt;Infrastructure as Code&lt;/strong&gt; piece in here, so I used Terraform’s Kubernetes provider to create the namespace.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;terraform/main.tf&lt;/code&gt; (provider + versions) and &lt;code&gt;terraform/k8s.tf&lt;/code&gt; (namespace resource) are already in the repo.&lt;/p&gt;

&lt;p&gt;If your &lt;code&gt;~/.kube/config&lt;/code&gt; points at a running cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform

terraform init
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is small on purpose, but it’s enough to say &lt;strong&gt;“I manage part of the Kubernetes infrastructure with Terraform”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s what my &lt;code&gt;terraform init&lt;/code&gt; + &lt;code&gt;terraform plan&lt;/code&gt; looked like:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F58cvarz0c17dkp5sahw2.png" class="article-body-image-wrapper"&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%2Farticles%2F58cvarz0c17dkp5sahw2.png" alt="Terraform init/plan output" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7) How can you reuse this
&lt;/h2&gt;

&lt;p&gt;If you want to adapt this project for yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fork the repo or copy the layout&lt;/li&gt;
&lt;li&gt;Change the &lt;strong&gt;Docker Hub&lt;/strong&gt; username and repo name&lt;/li&gt;
&lt;li&gt;Update:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;k8s/deployment.yaml&lt;/code&gt; &lt;code&gt;image:&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;GitHub Actions secrets (&lt;code&gt;DOCKERHUB_USERNAME&lt;/code&gt;, &lt;code&gt;DOCKERHUB_TOKEN&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Swap the Flask app for your own service if you like&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The nice part is that the pattern stays the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;App → Docker → Docker Hub → Kubernetes → (optional) Terraform&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once this pipeline is in your portfolio, you can honestly tell people:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I’ve built and maintained an end-to-end CI/CD pipeline with GitHub Actions, Docker, Kubernetes, Docker Hub, and Terraform. Here’s the repo and here’s the running app.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;This project is small, but it touches a lot of the buzzwords you see in job posts and freelance gigs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Docker Hub&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re trying to break into DevOps or just want something concrete to show, feel free to &lt;strong&gt;clone my &lt;a href="https://github.com/rufilboss/devops-e2e-pipeline" rel="noopener noreferrer"&gt;repo&lt;/a&gt;, run it locally, and then customize it&lt;/strong&gt; to match your own style and stack.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cicd</category>
      <category>docker</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Are You Planning to Get Into DevOps in 2026? A Practical Guide for the Real World</title>
      <dc:creator>Ilyas Rufai</dc:creator>
      <pubDate>Wed, 24 Dec 2025 21:57:09 +0000</pubDate>
      <link>https://forem.com/rufilboss/are-you-planning-to-get-into-devops-in-2026-a-practical-guide-for-the-real-world-3blb</link>
      <guid>https://forem.com/rufilboss/are-you-planning-to-get-into-devops-in-2026-a-practical-guide-for-the-real-world-3blb</guid>
      <description>&lt;p&gt;It’s &lt;strong&gt;December 2025&lt;/strong&gt;, almost January 2026, and if you’re asking yourself &lt;em&gt;“Should I start DevOps now?”&lt;/em&gt; Then this article is for you.&lt;/p&gt;

&lt;p&gt;Not because DevOps is &lt;em&gt;trending&lt;/em&gt; (it is, most companies now practice DevOps workflows and advanced automation standards), but because the &lt;em&gt;demand for real DevOps skill — not surface-level tool knowledge —&lt;/em&gt; is only going to grow faster in the AI-augmented era we’re entering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Now is the Right Time (But Also the Hardest Time)
&lt;/h2&gt;

&lt;p&gt;Over the last five years, DevOps has shifted from pure automation to &lt;strong&gt;intelligent automation&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitOps — treating Git as the &lt;em&gt;single source of truth&lt;/em&gt; for infrastructure and app deployments — is becoming mainstream.&lt;/li&gt;
&lt;li&gt;Cloud-native, serverless, and event-driven designs are reshaping how systems are built.&lt;/li&gt;
&lt;li&gt;AI is now embedded in pipelines, observability, and even predictive deployments, automating decisions that used to require senior engineers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The payoff? Teams deploy faster, with fewer errors, and automation frees engineers from repetitive toil.&lt;br&gt;
The catch? &lt;strong&gt;AI can mask gaps in understanding.&lt;/strong&gt; You can have something running and still not know &lt;em&gt;why&lt;/em&gt; it runs, and that’s where most beginners crash.⁣&lt;/p&gt;

&lt;p&gt;In 2026, &lt;em&gt;DevOps without deep fundamentals&lt;/em&gt; is like driving a car you can’t fix when it breaks.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Works (Based on Real Industry Signals)
&lt;/h2&gt;

&lt;p&gt;Here’s what industry trends say about DevOps evolution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitOps adoption&lt;/strong&gt; is expected to grow sharply, improving reliability and consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevSecOps&lt;/strong&gt; — embedding security into every pipeline is essential, not optional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observers and AI-powered observability tools&lt;/strong&gt; help engineers find problems before they hit users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless pipelines&lt;/strong&gt; are reshaping how we think about infrastructure abstraction and event wiring.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren’t buzzwords; they are what you’ll see in job descriptions, production systems, and real-world infrastructure teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  But First — A Reality Check
&lt;/h2&gt;

&lt;p&gt;Before we talk tools: DevOps is &lt;strong&gt;not a checklist&lt;/strong&gt; of technologies. It’s a &lt;em&gt;cultural and engineering mindset&lt;/em&gt; rooted in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Continuous Delivery&lt;/li&gt;
&lt;li&gt;Automated, high-confidence pipelines&lt;/li&gt;
&lt;li&gt;Reproducible infrastructure&lt;/li&gt;
&lt;li&gt;Collaboration between development and operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI will help you generate configs, but it &lt;strong&gt;won’t give you intuition&lt;/strong&gt; around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why a pipeline failed&lt;/li&gt;
&lt;li&gt;How a Kubernetes pod behaves under pressure&lt;/li&gt;
&lt;li&gt;What happens when your Terraform state drifts&lt;/li&gt;
&lt;li&gt;Why observability matters more than logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That intuition is what separates &lt;em&gt;copy-paste&lt;/em&gt; from &lt;em&gt;craftsmanship&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Practical DevOps Path
&lt;/h2&gt;

&lt;p&gt;Here’s how you should think about your journey if you’re starting &lt;strong&gt;now&lt;/strong&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Master Linux and Command Line
&lt;/h3&gt;

&lt;p&gt;Everything in DevOps runs on CLI, shells, permissions, networking, logs, and processes. Your laptop is your first “cluster.”&lt;br&gt;
If you can’t diagnose why SSH fails, nothing else will help.&lt;/p&gt;

&lt;p&gt;In 2026, this fundamental still underpins container workloads, cloud instances, observability agents, and automation scripts.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Scripting &amp;amp; Automation
&lt;/h3&gt;

&lt;p&gt;If you automate only once manually, that’s already DevOps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash/Python/Go for straightforward automation&lt;/li&gt;
&lt;li&gt;Tools like Make, just to organize tasks&lt;/li&gt;
&lt;li&gt;A mindset of &lt;em&gt;automate early, fix once&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI can &lt;em&gt;suggest scripts&lt;/em&gt; — but always read and refactor them yourself.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Containers &amp;amp; Microservices
&lt;/h3&gt;

&lt;p&gt;Docker, Kubernetes, and sidecar architectures aren’t going anywhere.&lt;br&gt;
These technologies form the backbone of cloud-native systems and service distribution.&lt;/p&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Docker makes shipping easier; Kubernetes makes scaling easier —&lt;br&gt;
you still need to know &lt;em&gt;what each component does&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  4. Real Cloud Practice (Practically)
&lt;/h3&gt;

&lt;p&gt;Cloud isn’t just AWS, Azure, or GCP; it’s &lt;em&gt;how production systems run&lt;/em&gt;. AI-native cloud services are replacing old workloads, and multi-cloud/hybrid strategies are increasingly popular.&lt;/p&gt;

&lt;p&gt;Instead of incurring huge bills while you learn, consider tools like &lt;strong&gt;LocalStack&lt;/strong&gt; (&lt;a href="https://github.com/localstack/localstack" rel="noopener noreferrer"&gt;https://github.com/localstack/localstack&lt;/a&gt;) — it lets you &lt;em&gt;practice cloud APIs locally&lt;/em&gt; without risk. That’s real hands-on learning.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. CI/CD and Beyond (From Commit to Deployment)
&lt;/h3&gt;

&lt;p&gt;This is where theory intersects practice.&lt;/p&gt;

&lt;p&gt;You won’t just be writing pipelines, you treat them like &lt;strong&gt;policies&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;Security scanning&lt;/li&gt;
&lt;li&gt;Version-controlled provisioning&lt;/li&gt;
&lt;li&gt;Fast rollback strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2026, AI isn’t replacing CI/CD engineers, it’s &lt;em&gt;augmenting&lt;/em&gt; them via smart predictions and autonomous decision loops.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Security &amp;amp; Resilience
&lt;/h3&gt;

&lt;p&gt;DevSecOps is now standard practice:&lt;br&gt;
Security checks must happen &lt;em&gt;before&lt;/em&gt; deployments, not after!&lt;/p&gt;

&lt;p&gt;You need broad fluency with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secrets management (Vault, AWS Secrets Manager)&lt;/li&gt;
&lt;li&gt;Policy as code (OPA, Kyverno)&lt;/li&gt;
&lt;li&gt;Automated vulnerability scanning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security is not a separate discipline; it’s a microbial process embedded throughout your pipelines.&lt;/p&gt;




&lt;h2&gt;
  
  
  How AI Helps (And When It Hurts)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✔️ Where AI Adds Massive Value
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Generating boilerplate Terraform/CI/CD&lt;/li&gt;
&lt;li&gt;Explaining error logs in clear language&lt;/li&gt;
&lt;li&gt;Offering alternative solutions fast&lt;/li&gt;
&lt;li&gt;Flagging potential configuration problems early&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Where AI Can Mislead
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Blindly copying code without understanding it&lt;/li&gt;
&lt;li&gt;Masking architectural tradeoffs&lt;/li&gt;
&lt;li&gt;Assuming generated configs are optimal&lt;/li&gt;
&lt;li&gt;Trusting AI output without testing it thoroughly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt;&lt;br&gt;
If AI helps you &lt;em&gt;understand&lt;/em&gt; a solution — it’s good.&lt;br&gt;
If it only helps you &lt;em&gt;run&lt;/em&gt; a solution — it’s dangerous.&lt;/p&gt;




&lt;h2&gt;
  
  
  These Resources Will Accelerate Your Journey
&lt;/h2&gt;

&lt;p&gt;You might find these extremely useful:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;100 Days of DevOps&lt;/strong&gt; — a proven, hands-on progression:&lt;br&gt;
&lt;a href="https://github.com/rufilboss/100DaysOfDevOps" rel="noopener noreferrer"&gt;https://github.com/rufilboss/100DaysOfDevOps&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;DevOps Guide (fork &amp;amp; extend it):&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/rufilboss/DevOps-Guide/" rel="noopener noreferrer"&gt;https://github.com/rufilboss/DevOps-Guide/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;DevOps Books curated by the community:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/DevOps-Projects-Ideas/DevOps-Books/" rel="noopener noreferrer"&gt;https://github.com/DevOps-Projects-Ideas/DevOps-Books/&lt;/a&gt;&lt;br&gt;
(500+ stars | 270+ forks — real community validation)&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Cloud infrastructure local practice (Low cost/no risk):&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/localstack/localstack" rel="noopener noreferrer"&gt;https://github.com/localstack/localstack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are not ads, they are &lt;strong&gt;battle-tested resources&lt;/strong&gt; used by beginners and pros alike.&lt;/p&gt;




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

&lt;p&gt;DevOps in 2026 is not &lt;em&gt;just another career&lt;/em&gt;.&lt;br&gt;
It’s about &lt;strong&gt;thinking in systems&lt;/strong&gt;, not tools.&lt;/p&gt;

&lt;p&gt;AI will let you &lt;em&gt;explore more&lt;/em&gt;, but only fundamentals will let you &lt;em&gt;master more&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You don’t need to learn everything today, but you do need to build &lt;strong&gt;confidence through repetition, testing, failure, and curiosity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you’re ready to dive in, you’re already ahead of the crowd.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>beginners</category>
      <category>ai</category>
      <category>cloud</category>
    </item>
    <item>
      <title>A Practical Guide to Organizing an AWS Community Day (From Scratch)</title>
      <dc:creator>Ilyas Rufai</dc:creator>
      <pubDate>Tue, 23 Dec 2025 11:59:50 +0000</pubDate>
      <link>https://forem.com/rufilboss/a-practical-guide-to-organizing-an-aws-community-day-from-scratch-1ao4</link>
      <guid>https://forem.com/rufilboss/a-practical-guide-to-organizing-an-aws-community-day-from-scratch-1ao4</guid>
      <description>&lt;h2&gt;
  
  
  What is AWS Community Day?
&lt;/h2&gt;

&lt;p&gt;AWS Community Day is a &lt;strong&gt;one-day, community-led conference&lt;/strong&gt; organized by AWS communities such as AWS User Groups or AWS Cloud Clubs. It brings cloud practitioners, students, and enthusiasts together to learn, network, and share real-world AWS knowledge.&lt;/p&gt;

&lt;p&gt;Community Days range from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large, multi-country events (e.g., AWS Community Day DACH)&lt;/li&gt;
&lt;li&gt;To smaller, single-community events organized by one User Group or Cloud Club&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide documents &lt;strong&gt;what it takes to plan, organize, and execute an AWS Community Day successfully&lt;/strong&gt;, especially if you’re doing it for the &lt;strong&gt;first time&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 1: Foundations (Before Anything Else)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Define Your Scope
&lt;/h3&gt;

&lt;p&gt;Before tools, sponsors, or speakers, be clear on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Target audience:&lt;/strong&gt; students, professionals, beginners, mixed?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event size goal:&lt;/strong&gt; (e.g. 1000–1500 attendees)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event type:&lt;/strong&gt; free or paid&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Location:&lt;/strong&gt; city, campus, venue type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Placeholder:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Expected attendees: ___&lt;br&gt;
Target audience: ___&lt;br&gt;
City/Campus: ___&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2. Official AWS Alignment (Very Important)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  AWS Community Day Page
&lt;/h4&gt;

&lt;p&gt;Start here:&lt;br&gt;
👉 &lt;a href="https://aws.amazon.com/developer/community/community-day/" rel="noopener noreferrer"&gt;https://aws.amazon.com/developer/community/community-day/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This page explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What qualifies as an AWS Community Day&lt;/li&gt;
&lt;li&gt;Branding rules&lt;/li&gt;
&lt;li&gt;FAQs&lt;/li&gt;
&lt;li&gt;Organizer expectations&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  AWS Organizer Slack
&lt;/h4&gt;

&lt;p&gt;Join the &lt;strong&gt;&lt;code&gt;community-day-organizers&lt;/code&gt;&lt;/strong&gt; Slack channel.&lt;br&gt;
This is critical for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoiding date clashes in the same region&lt;/li&gt;
&lt;li&gt;Learning from other organizers&lt;/li&gt;
&lt;li&gt;Funding guidance&lt;/li&gt;
&lt;li&gt;Shared templates and experiences&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Phase 2: Online Presence &amp;amp; Registration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3. Event Website
&lt;/h3&gt;

&lt;p&gt;You will need a simple website containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event details&lt;/li&gt;
&lt;li&gt;Agenda&lt;/li&gt;
&lt;li&gt;Speakers&lt;/li&gt;
&lt;li&gt;Registration link&lt;/li&gt;
&lt;li&gt;Sponsors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build your own&lt;/li&gt;
&lt;li&gt;Use a template (e.g. Hugo-based AWS Community Day templates)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Placeholder:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Website URL: ___&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  4. Registration
&lt;/h3&gt;

&lt;p&gt;Common tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eventbrite&lt;/li&gt;
&lt;li&gt;Google Forms&lt;/li&gt;
&lt;li&gt;Konfhub&lt;/li&gt;
&lt;li&gt;Other ticketing platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose one that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles attendee limits&lt;/li&gt;
&lt;li&gt;Exports attendee data&lt;/li&gt;
&lt;li&gt;Supports QR codes (nice bonus)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Placeholder:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Registration platform: ___&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  5. Call for Speakers (CfS)
&lt;/h3&gt;

&lt;p&gt;You’ll need speakers early.&lt;/p&gt;

&lt;p&gt;Tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sessionize&lt;/li&gt;
&lt;li&gt;Google Forms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Collect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Topic title&lt;/li&gt;
&lt;li&gt;Abstract&lt;/li&gt;
&lt;li&gt;Speaker bio&lt;/li&gt;
&lt;li&gt;Preferred time slot&lt;/li&gt;
&lt;li&gt;Session level (Beginner / Intermediate / Advanced)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Placeholder:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CfS link: ___&lt;br&gt;
Submission deadline: ___&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Phase 3: AWS Support &amp;amp; Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6. AWS-Provided Materials
&lt;/h3&gt;

&lt;p&gt;AWS provides downloadable assets that help a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logos&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;Slide templates&lt;/li&gt;
&lt;li&gt;Organizer resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UG Toolkit / Community Toolkit&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. Funding (Yes, It’s Possible 💵)
&lt;/h3&gt;

&lt;p&gt;AWS may provide &lt;strong&gt;financial or material support&lt;/strong&gt; depending on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event size&lt;/li&gt;
&lt;li&gt;Community maturity&lt;/li&gt;
&lt;li&gt;Region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Funding requests are usually discussed in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS organizer Slack&lt;/li&gt;
&lt;li&gt;Via AWS Community contacts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Placeholder:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Funding requested? Yes / No&lt;br&gt;
Amount / type: ___&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Phase 4: Planning the Actual Event
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8. Attendee Estimation (Be Realistic)
&lt;/h3&gt;

&lt;p&gt;Estimating attendance is tricky.&lt;/p&gt;

&lt;p&gt;Consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Size of your community&lt;/li&gt;
&lt;li&gt;Average meetup attendance&lt;/li&gt;
&lt;li&gt;Travel distance&lt;/li&gt;
&lt;li&gt;Marketing reach&lt;/li&gt;
&lt;li&gt;Exam periods / holidays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rule of thumb:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Expect less → Be pleasantly surprised later&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also expect &lt;strong&gt;last-minute registrations&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. Venue Selection
&lt;/h3&gt;

&lt;p&gt;Choose a venue that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can scale up or down&lt;/li&gt;
&lt;li&gt;Supports multiple rooms&lt;/li&gt;
&lt;li&gt;Has reliable power &amp;amp; internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimum rooms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Main session room(s)&lt;/li&gt;
&lt;li&gt;Speakers’ room&lt;/li&gt;
&lt;li&gt;Storage / quiet room&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Tip:&lt;/em&gt;&lt;br&gt;
Sponsors/expo area should be &lt;strong&gt;where attendees naturally pass&lt;/strong&gt;, not isolated.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. Catering
&lt;/h3&gt;

&lt;p&gt;Keep it simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snacks&lt;/li&gt;
&lt;li&gt;Lunch&lt;/li&gt;
&lt;li&gt;Drinks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t forget:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speakers’ room refreshments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Placeholder:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Catering plan: ___&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  11. Tracks &amp;amp; Agenda Design
&lt;/h3&gt;

&lt;p&gt;Less is more.&lt;/p&gt;

&lt;p&gt;Avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too many parallel tracks&lt;/li&gt;
&lt;li&gt;Overlapping highly attractive sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended format (per session):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30 min talk&lt;/li&gt;
&lt;li&gt;15 min Q&amp;amp;A&lt;/li&gt;
&lt;li&gt;15 min break / expo&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sample Agenda
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;08:00 – Registration
09:00 – Opening Remarks
09:15 – Keynote
10:00 – Break / Expo
10:30 – Session Slot 1
11:30 – Session Slot 2
12:30 – Lunch
13:30 – Session Slot 3
14:30 – Session Slot 4
15:30 – Break
16:00 – Final Session
16:30 – Closing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  12. Speakers
&lt;/h3&gt;

&lt;p&gt;Aim for balance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS employees&lt;/li&gt;
&lt;li&gt;Community speakers&lt;/li&gt;
&lt;li&gt;First-time speakers&lt;/li&gt;
&lt;li&gt;Local &amp;amp; external speakers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preferred speaking time&lt;/li&gt;
&lt;li&gt;Travel needs&lt;/li&gt;
&lt;li&gt;Slide sharing permission&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  13. Free vs Paid Event
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Free Event&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher no-shows&lt;/li&gt;
&lt;li&gt;More inclusive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Paid Event&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fewer no-shows&lt;/li&gt;
&lt;li&gt;More admin (tax, accounting)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose what fits your context.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 5: Marketing &amp;amp; Sponsors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  14. Marketing (Don’t Underestimate This)
&lt;/h3&gt;

&lt;p&gt;Channels to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WhatsApp groups&lt;/li&gt;
&lt;li&gt;LinkedIn&lt;/li&gt;
&lt;li&gt;Twitter/X&lt;/li&gt;
&lt;li&gt;Campus clubs&lt;/li&gt;
&lt;li&gt;Word of mouth&lt;/li&gt;
&lt;li&gt;Sponsors’ channels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start early.&lt;/p&gt;




&lt;h3&gt;
  
  
  15. Sponsors
&lt;/h3&gt;

&lt;p&gt;Sponsors fund your event.&lt;/p&gt;

&lt;p&gt;Prepare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear sponsorship packages&lt;/li&gt;
&lt;li&gt;Benefits list (booth, logo, talk, etc.)&lt;/li&gt;
&lt;li&gt;Venue layout&lt;/li&gt;
&lt;li&gt;Simple agreement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some sponsors may qualify for &lt;strong&gt;AWS MDF funding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;📌 &lt;em&gt;Placeholder:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sponsor tiers: ___&lt;br&gt;
Confirmed sponsors: ___&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Phase 6: Operations &amp;amp; Logistics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  16. Organizing Team
&lt;/h3&gt;

&lt;p&gt;Small teams can work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2–5 core organizers&lt;/li&gt;
&lt;li&gt;Clear roles (logistics, speakers, sponsors, media)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  17. Volunteers
&lt;/h3&gt;

&lt;p&gt;Volunteers help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registration&lt;/li&gt;
&lt;li&gt;Directions&lt;/li&gt;
&lt;li&gt;Speaker support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tip:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ask sponsors if they can assign volunteers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  18. Badges, Lanyards &amp;amp; Printing
&lt;/h3&gt;

&lt;p&gt;Decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-printed vs on-site printing&lt;/li&gt;
&lt;li&gt;Badge color coding (Organizers, Speakers, Sponsors, Attendees)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reusable materials save money long-term.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 7: Communication &amp;amp; Experience
&lt;/h2&gt;

&lt;h3&gt;
  
  
  19. Communication Channels
&lt;/h3&gt;

&lt;p&gt;Recommended:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slack (organizers + speakers)&lt;/li&gt;
&lt;li&gt;WhatsApp (organizers + volunteers)&lt;/li&gt;
&lt;li&gt;WhatsApp (real-time event coordination)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  20. Speaker Slides &amp;amp; Content
&lt;/h3&gt;

&lt;p&gt;Attendees often ask for slides.&lt;/p&gt;

&lt;p&gt;Before the event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask speakers if slides can be shared
After the event:&lt;/li&gt;
&lt;li&gt;Upload to website or shared drive&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  21. Speaker Dinner (Highly Recommended)
&lt;/h3&gt;

&lt;p&gt;If budget allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host a speaker dinner&lt;/li&gt;
&lt;li&gt;Builds relationships&lt;/li&gt;
&lt;li&gt;Improves speaker experience&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Reality Check 😅
&lt;/h2&gt;

&lt;p&gt;People will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complain&lt;/li&gt;
&lt;li&gt;Ask last-minute questions&lt;/li&gt;
&lt;li&gt;Need help&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is normal. Expect it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Organizing an AWS Community Day is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hard work&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time-consuming&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extremely rewarding&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From idea to execution, expect &lt;strong&gt;months of planning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you’re thinking about doing it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Yes — do it.&lt;/strong&gt;&lt;br&gt;
The impact on your community is worth it.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>awscloud</category>
      <category>cloudcomputing</category>
      <category>awscommunityday</category>
    </item>
  </channel>
</rss>
